如何在STLpriority_queue中进行高效的优先级更新?
我有某个对象的priority_queue:
typedef priority_queue<Object> Queue;
Queue queue;
有时,其中一个对象的优先级可能会改变 - 我需要能够以有效的方式更新队列中该对象的优先级。 目前我正在使用这种方法,它有效但似乎效率低下:
Queue newQueue;
while (!queue.empty())
{
Object obj=queue.top();
queue.pop();
if (priorityHasChanged(obj))
newQueue.push_back(Object(new_priority));
else
newQueue.push_back(obj);
}
newQueue.swap(queue); // this only works because I actually subclassed the priority_queue
// class and exposed a swap method that swaps in the container
我以这种方式实现它是因为我当时很着急,这是我能做的最快的事情,我可以确定它会正常工作。 但必须有比这更好的方法。 我真正想要的是一种方法:
- 提取具有更改的优先级的实例,并插入具有新优先级值的新实例,
- 更新具有更改的优先级的实例,然后更新队列,以便它正确排序
什么是最好的方法来做到这一点?
I have a priority_queue of some object:
typedef priority_queue<Object> Queue;
Queue queue;
From time to time, the priority of one of the objects may change - I need to be able to update the priority of that object in the queue in an efficient way. Currently I am using this method which works but seems inefficient:
Queue newQueue;
while (!queue.empty())
{
Object obj=queue.top();
queue.pop();
if (priorityHasChanged(obj))
newQueue.push_back(Object(new_priority));
else
newQueue.push_back(obj);
}
newQueue.swap(queue); // this only works because I actually subclassed the priority_queue
// class and exposed a swap method that swaps in the container
I implemented it this way because I was in kind of a hurry at the time and this was the quickest thing I could do that I could be sure it would work ok. There has to be a better way than this though. Really what I want is a way to either:
- extract out the instance with the changed priority and insert a new one with the new priority value
- update the instance with the changed priority and then update the queue so that it is correctly sorted
What is the best way to do this?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(7)
我可以建议两种选择来解决问题,尽管两者都没有执行真正的更新。
每次您想要更新时,请使用
priority_queue
并推送元素。 接受这样一个事实:队列中将有无用的条目。 弹出顶部值时,检查它是否包含最新值。 如果没有,请忽略它并弹出下一个。通过这种方式,您可以延迟删除更新的元素,直到它到达顶部。 我注意到实现 Dijkstra 算法的顶级程序员正在使用这种方法。
使用
设置
。 它还经过排序,以便您能够在对数时间内提取最大元素。 您还可以在再次插入之前删除过时的元素。 因此仍然无法进行更新操作,但可以删除和重新插入。似乎两种方法的复杂性是相同的。
I can suggest 2 choices to solve the problem, although neither performs a real update.
Use the
priority_queue
and push element each time you would like to update it. Accept the fact that you will have useless entries in the queue. When popping the top value, check if it contains the up-to-date value. If not, ignore it and pop the next.This way you delay the removal of the updated element until it comes to the top. I noticed this approach being used by top programmers realizing Dijkstra algorithm.
Use
set
. It is also sorted so you are able to extract the greatest element in logarithmic time. You are also able to remove the outdated element before inserting it again. So still no update operation possible, but removal and reinsertion is doable.Seems like the complexity of both approaches is the same.
我认为你对标准优先级队列不走运,因为你无法获得底层的双端队列/向量/列表或其他东西。 您需要实现自己的 - 这并不难。
I think you are out of luck with standard priority queue because you can't get at the underlying deque/vector/list or whatever. You need to implement your own - it's not that hard.
我实现了一个高性能可更新优先级队列,并在github上提供了它。
这就是您通常使用它的方式:
为了补充 Jarekczek 的答案,如果
set
和“带有无用条目的纯堆”方法确实具有相同的复杂性,则stl::set
code> 版本的执行速度通常比stl::priority_queue
版本慢得多,因为它是用红黑树实现的,只能保证其深度低于 2*log_2(n_elements) 并且需要定期更新,而stl::priority_queue
是尽可能纯粹且快速的二进制堆。 这就是为什么在实现 Dijkstra 时通常使用它。然而,当在少数基本节点上进行大量更新时,集合方法可能会更快。 这也是使用我的库会给您带来最大改进的地方。
I have implemented a high-performance updatable priority queue and made it available on github.
This is how you would typically use it :
To complement Jarekczek's answer, if indeed both the
set
and "pure heap with useless entries" approaches have the same complexity, thestl::set
version typically performs much slower than thestl::priority_queue
version due to the fact that it is implemented with red-black trees that only guarantee their depth to be lower than 2*log_2(n_elements) and require regular updates, whilestl::priority_queue
is an as pure and fast binary heap as possible. This is why it is typically used when implementing Dijkstra.The set approach may however be faster when making a lot of updates on few base nodes. This is also where using my library would bring you the most improvement.
适当的数据结构称为“斐波那契堆”。
但需要你自己去实现。
插入/更新是 O(1)
ExtractMin 为 O(logn)
The appropriate data structure is called "Fibonacci Heap".
But you need to implement it yourself.
Insert/Updates are O(1)
ExtractMin is O(logn)
使用 STL(据我所知)执行此操作的最直接方法是删除该条目,更新其优先级,然后重新插入它。 使用 std::priority_queue 执行此操作非常慢,但是您可以使用 std::set 执行相同的操作。 不幸的是,当对象在集合中时,您必须小心不要更改对象的优先级。
我已经实现了基于 mutable_priority_queue 类,将 std::multimap (用于优先级到值映射)和 std::map (用于值到优先级映射)粘合在一起,允许您插入/删除项目以及更新对数中的现有值时间。 您可以在此处获取代码和如何使用它的示例
The most straightforward way to do this with STL (that I know) is to remove the entry, update its priority and then reinsert it. Doing this is quite slow using std::priority_queue, however you can do the same thing with std::set. Unfortunately you have to be careful to not change the priority of an object when it is in the set.
I have implemented a mutable_priority_queue class based gluing together an std::multimap (for priority to value mapping) and an std::map (for value to priority mapping) that allows you to insert/remove items as well as update existing values in logarithmic time. You can get the code and an example of how to use it here
您可能想查看
replace_if
以及此处的示例。You may want to have a look at
replace_if
with an example here.不幸的是,您无法更新priority_queue中的值。 priority_queue不提供这样的接口。
我认为你最好像 Jarekczek 所说的那样使用
set
或使用 这个解决方案(使用 make_heap) 。Unfortunately you cannot update value in priority_queue. priority_queue does not offer such interface.
I think you'd better use
set
as Jarekczek said or use this solution(using make_heap).