如何告诉 std::priority_queue 刷新其顺序?
我有一个指向struct city
的指针的优先级队列。我修改优先级队列外部这些指针指向的对象,并希望告诉优先级队列根据新值“重新排序”自身。
我应该怎么办?
例子:
#include <iostream>
#include <queue>
using namespace std;
struct city {
int data;
city *previous;
};
struct Compare {
bool operator() ( city *lhs, city *rhs )
{
return ( ( lhs -> data ) >= ( rhs -> data ) );
}
};
typedef priority_queue< city *, vector< city * >, Compare > pqueue;
int main()
{
pqueue cities;
city *city1 = new city;
city1 -> data = 5;
city1 -> previous = NULL;
cities.push( city1 );
city *city2 = new city;
city2 -> data = 3;
city2 -> previous = NULL;
cities.push( city2 );
city1 -> data = 2;
// Now how do I tell my priority_queue to reorder itself so that city1 is at the top now?
cout << ( cities.top() -> data ) << "\n";
// 3 is printed :(
return 0;
}
I have a priority queue of pointers to a struct city
. I modify the objects pointed by these pointers outside the priority queue, and want to tell the priority queue to "reorder" itself according to the new values.
What should I do?
Example:
#include <iostream>
#include <queue>
using namespace std;
struct city {
int data;
city *previous;
};
struct Compare {
bool operator() ( city *lhs, city *rhs )
{
return ( ( lhs -> data ) >= ( rhs -> data ) );
}
};
typedef priority_queue< city *, vector< city * >, Compare > pqueue;
int main()
{
pqueue cities;
city *city1 = new city;
city1 -> data = 5;
city1 -> previous = NULL;
cities.push( city1 );
city *city2 = new city;
city2 -> data = 3;
city2 -> previous = NULL;
cities.push( city2 );
city1 -> data = 2;
// Now how do I tell my priority_queue to reorder itself so that city1 is at the top now?
cout << ( cities.top() -> data ) << "\n";
// 3 is printed :(
return 0;
}
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(5)
这有点骇人听闻,但并不违法,而且它完成了工作。
更新:
如果出现以下情况,请勿使用此 hack:
vector
。Compare
函子的行为会导致您的外部副本的顺序与存储在priority_queue
内的Compare
副本的顺序不同。您始终可以编写自己的容器适配器来包装堆算法。
priority_queue
只不过是make/push/pop_heap
的一个简单包装器。This is a bit hackish, but nothing illegal about it, and it gets the job done.
Update:
Do not use this hack if:
vector
.Compare
functor has behavior that would cause your external copy to order differently than the copy ofCompare
stored inside thepriority_queue
.You can always write your own container adaptor which wraps the heap algorithms.
priority_queue
is nothing but a simple wrapper aroundmake/push/pop_heap
.如果您需要保留有序集合,您可以考虑以下解决方案:使用
std::set
并更新值,删除该项目,更新其值并将其放回集合中。这将为您提供更新一项的 O(log n) 复杂度,这是在通常的堆结构中所能做到的最好的复杂度(假设您可以通过 sift-up-sift-down 过程访问其内部以进行质量)。std::set
的唯一缺点是初始化包含 n 个项目的集合的时间。 (O(n log n) 而不是 O(n))。If you need to keep an ordered collection you may consider the following solution: Use
std::set
and to update values remove the item, update its value and place it back into the set. This will give you O(log n) complexity for updating one item, which is the best you can in a usual heap structure (Assuming you had access to its internals to mass with the sift-up sift-down procedures).The only downside to
std::set
will be the time for initializing a set with n items. (O(n log n) instead of O(n)).基于http://www.sgi.com/tech/stl/priority_queue.html 看起来没有办法做到这一点,无需清空并重新插入。
如果您愿意放弃priority_queue(但仍然想要堆),那么您可以使用向量以及make_heap、push_heap 和 pop_heap。请参阅 priority_queue 页面中的注释部分。
Based on http://www.sgi.com/tech/stl/priority_queue.html it does not look like there is a way to do that, without emptying and re-inserting.
If you are willing to move away from priority_queue (but still want a heap), then you can use a vector, along with the make_heap, push_heap and pop_heap. See the Notes section in the page for priority_queue.
这是一个老问题,但当我想自己做这个时,我对任何答案都不完全满意。不需要任何黑客攻击。
std::priority_queue
包含合法且惯用地执行此操作的所有机制。std::priority_queue
有两个非常有用的数据成员:c
(底层容器)和comp
(比较谓词)。同样有用的是,该标准要求
Container
模板类型必须是SequenceContainer
的模型,而其迭代器是RandomAccessIterator
的模型。这很有用,因为
std::make_heap
的Iter
参数类型具有相同的RandomAccessIterator
模型要求。这是一种冗长的说法,std::priority_queue 是堆的包装器,因此 std::make_heap(std::begin(c), std::end(c) ), comp) 必须是有效的表达式。
“坏”消息是
c
和comp
受到保护。这实际上是个好消息,原因有两个:您不能意外破坏堆。
如果您从
std::priority_queue
派生,您可以有意修改堆。因此,诀窍是从
std::priority_queue
派生优先级队列,在成员函数中,以您喜欢的任何方式改变内部堆c
,然后调用std ::make_heap(std::begin(c), std::end(c), comp);
将其转回有效堆。嗯,是的,但是……
对于年轻人和/或粗心的人来说,这可能是一个坏主意,原因有两个。缺乏多态析构函数和切片风险。
通过指向其基类的指针来拥有 std 容器实际上没有合理的用例。集装箱重量轻(当里面没有任何东西时)并且移动成本低廉。您也许能够想到用例,但我可以保证无论您打算做什么,都可以通过将容器封装在另一个堆分配的对象中来更好地完成。在设计良好的代码中,这永远不应该成为一个问题。无论如何,从
priority_queue
模板类私有继承可以消除这种风险。当然,当我们传递继承的对象时,存在切片的风险。这里的答案是从
priority_queue
基类私有继承,然后在派生类中使用using
来仅导出我们希望共享的基类接口部分。下面的示例已更新以显示这一点。
下面是一个来自真实项目的例子。
要求:
保留必须通知客户端更改的主题队列。按该主题最早收到通知的时间戳对队列进行排序。不允许重复的主题名称。
这是一个工作演示:
This is an old question but I wasn't fully satisfied with any of the answers when I wanted to do this myself. There is no need for any hacks.
std::priority_queue
contains all the machinery to do this legally and idiomatically.std::priority_queue
has two very helpful data members,c
(the underlying container) andcomp
(the comparison predicate).Equally helpfully, the standard mandates that the
Container
template type must be a model ofSequenceContainer
who's iterators are models ofRandomAccessIterator
.This is helpful because the
Iter
argument type ofstd::make_heap
have the sameRandomAccessIterator
model requirement.This is a longwinded way of saying that
std::priority_queue
is a wrapper around a heap and that thereforestd::make_heap(std::begin(c), std::end(c), comp)
must be a valid expression.The 'bad' news is that
c
andcomp
are protected. This is actually good news for two reasons:You can't destroy the heap accidentally.
If you derive from
std::priority_queue
you can modify the heap intentionally.So the trick is to derive your priority queue from
std::priority_queue
, in a member function, mutate the internal heapc
any way you like and then callstd::make_heap(std::begin(c), std::end(c), comp);
to turn it back into a valid heap.Well, yes, but...
There are two reasons that this could be a bad idea for the young and/or unwary. Lack of polymorphic destructors and the risk of slicing.
There is actually no reasonable use case for owning a std container through a pointer to its base class. Containers are lightweight (when there is nothing in them) and cheaply moveable. You may be able to think of use cases, but I can guarantee that whatever you intended to do can be done better by encapsulating the container in another heap-allocated object. In well-designed code, this should never have become a concern. In any case, inheriting privately from the
priority_queue
template class removes this risk.Certainly there is a risk of slicing when we pass inherited objects around. The answer here is to inherit privately from the
priority_queue
base class, and then useusing
in the derived class to export only the parts of the base class's interface that we wish to share.The example below has been updated to show this.
Below is an example from a real project.
Requirements:
Keep a queue of topics that a client must be notified changes to. Order the queue by the timestamp of the earliest time that this topic was notified. Do not allow duplicate topic names.
Here is a working demo:
stl 的容器不提供尽可能快的可更新优先级队列。
@Richard Hodges:
make_heap
的复杂度为 O(n),并且push_heap
函数不会告诉您所提供的元素存储在哪里,从而可以快速更新单个元素不可能(你需要 O(n) 才能找到它)。我实现了一个高性能的可更新优先级队列(更新成本为 O(log n),是使用
std::set
的两倍)并使其可用 在 github 上。这是您通常使用它的方式:
The stl's containers don't provide as fast as possible updatable priority queues.
@Richard Hodges:
make_heap
takes O(n) complexity, and thepush_heap
function doesn't tell you where the provided element was stored, making a quick update of a single element impossible (you need O(n) to find it).I have implemented a high-performance updatable priority queue (an update costs O(log n), twice as fast as using an
std::set
) and made it available on github.This is how you would typically use it :