在优先级队列中随机访问

Randomly Access in a Priority Queue

本文关键字:随机 访问 队列 优先级      更新时间:2023-10-16

如何在优先级队列中随机访问/搜索?。例如,如果有一个优先级队列,比如q={5,4,3,2,1},例如,我想直接访问第三个值,也就是3,我不能这样做,在优先级队列中有随机访问的过程吗?

大多数优先级队列实现,包括C++std::priority_queue类型,都不支持随机访问。优先级队列背后的思想是牺牲随机访问来快速访问最小元素。

根据你想做的事情,你可以使用许多其他方法。如果总是想要访问队列中的第三个元素(而不是任何其他任意位置),那么只需将两个元素出列,缓存它们,然后将所需的值出列,并将其他两个元素放回就足够快了。

如果您想在任何时间点访问第k个最小的元素,其中k较大,一种选择是存储两个不同的优先级队列:一个包含k个元素的反向排序优先级队列(称为左队列)和一个包含其余n-k个元素(称为右队列)的常规优先级队列。要获得第k个最小元素,请从左队列出列(返回第k个最少元素),然后从右出列一个元素,并将其排入左队列,使其返回到总共k个元素。要进行排队,请检查该数字是否小于左侧队列的顶部。如果是,请从左侧队列出列,将删除的元素排入右侧队列,然后将原始元素排入左侧队列。否则,请向右排队。这保证了每个操作的运行时间为O(logn)。

如果您需要对排序序列进行真正的随机访问,请考虑使用顺序统计树。这是一个扩充的二进制搜索树,支持通过索引对元素进行O(logn)访问。您可以使用它来构建优先级队列-最小元素始终位于索引0处。问题是(当然也有问题)很难找到一个好的实现,并且隐藏在O(logn)项中的常数因子比标准二进制堆中的常数高得多。

添加到@templateypepedef的答案中:

除非使用效率非常低的优先级队列,否则不能将元素的随机访问与优先级队列相结合。根据您的需要,这里有几个选项:

1-一个低效的优先级队列将是一个保持排序的std::vector。推动一个元素意味着找到它应该插入的位置,并向前移动所有后续元素。弹出一个元素将简单地读取和删除最后一个元素(backpop_back,它们是有效的)。当然,随机访问也是有效的。

2-您可以使用std::multiset(或std::multimap)而不是优先级队列。它是一个保持事物有序的树状结构。您可以使用insert而不是push,然后使用带有erasebegin(或rbegin)迭代器读取并删除第一个(或最后一个)元素。插入和查找第一个/最后一个元素是log(n)操作。数据结构允许按顺序读取所有元素,尽管它不提供随机访问。

3-你可以使用std::vectorstd::push_heapstd::pop_heap算法(以及std::vectorpush_backpop_back方法)破解你自己版本的std::priority_queue。您将获得同样高效的优先级队列,但也可以随机访问priority_queue的所有元素。它们没有排序,您只知道数组中的第一个元素是最高优先级的元素,其他元素以满足堆属性的方式存储。如果只是偶尔想按顺序读取所有元素,可以使用函数std::sort_heap按优先级对数组中的所有元素进行排序。函数std::make_heap会将数组返回到其堆状态。

注意,默认情况下,std::priority_queue使用std::vector来存储其数据。reinterpret_caststd::priority_queuestd::vector可能是可行的,因此您可以随机访问队列中的其他元素。但是,如果它适用于标准库的实现,那么它可能不适用于其他库,也不适用于同一库的未来版本,所以我不建议您这样做!按照上面的#3,使用标准库中的算法创建自己的堆类更安全。