如何在STL priority_queue中进行有效的优先级更新?

时间:2009-03-16 08:31:50

标签: c++ stl priority-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

我是用这种方式实现的,因为我当时有点匆忙,这是我能做的最快的事情,我可以确定它能正常工作。不过必须有比这更好的方法。真的我想要的是一种方式:

  • 提取具有更改优先级的实例,并插入具有新优先级值的新实例
  • 使用更改的优先级更新实例,然后更新队列以使其正确排序

这样做的最佳方式是什么?

7 个答案:

答案 0 :(得分:43)

我可以建议2个选择来解决问题,尽管它们都没有执行真正的更新。

  1. 每次要更新时,请使用priority_queue和push元素。接受您将在队列中包含无用条目的事实。弹出最高值时,检查它是否包含最新值。如果没有,请忽略它并弹出下一个。

    这样,您可以延迟删除更新的元素,直到它到达顶部。我注意到这种方法被顶级程序员用来实现Dijkstra算法。

  2. 使用set。它也是排序的,因此您可以在对数时间内提取最大元素。您还可以在再次插入之前删除过时的元素。因此仍然无法进行更新操作,但删除和重新插入是可行的。

  3. 似乎两种方法的复杂性是相同的。

答案 1 :(得分:7)

我认为你对标准优先级队列运气不好,因为你无法获得底层的deque / vector / list或其他什么。你需要实现自己的 - 这并不难。

答案 2 :(得分:5)

使用STL(我知道)执行此操作的最直接方法是删除条目,更新其优先级,然后重新插入。使用std :: priority_queue执行此操作非常慢,但是您可以使用std :: set执行相同的操作。不幸的是,当它在集合中时,你必须小心不要改变对象的优先级。

我已经实现了一个mutable_priority_queue类,它将std :: multimap(用于优先级到值映射)和std :: map(用于值到优先级映射)粘合在一起,允许您插入/删除项目以及更新现有项目对数时间的值。您可以获取代码以及如何使用它的示例here

答案 3 :(得分:5)

适当的数据结构称为“Fibonacci Heap”。 但是你需要自己实现它。 插入/更新是O(1) ExtractMin是O(logn)

答案 4 :(得分:1)

我实现了一个高性能的可更新优先级队列,并使其可用on github

这是通常的使用方式:

better_priority_queue::updatable_priority_queue<int,int> pQ;
pQ.push(0, 30);   // or pQ.set(0, 30)
pQ.push(1, 20);
pQ.push(2, 10);

pQ.update(2, 25); // or pQ.set(2, 25)

while(!pQ.empty())
    std::cout << pQ.pop_value().key << ' ';

// Outputs: 0 2 1

要补充Jarekczek的回答,如果set和“纯堆无用条目”方法确实具有相同的复杂性,则stl::set版本的性能通常比stl::priority_queue版本慢得多由于它是用红黑树实现的,因此只能保证其深度小于2 * log_2(n_elements)并需要定期更新,而stl::priority_queue是尽可能纯净且快速的二进制堆。这就是为什么在实现Dijkstra时通常使用它。

但是,在几个基本节点上进行大量更新时,设置方法可能会更快。这也是使用我的图书馆可以为您带来最大进步的地方。

答案 5 :(得分:0)

您可能希望使用示例here查看replace_if

答案 6 :(得分:0)

很遗憾,您无法更新priority_queue中的值。 priority_queue不提供这样的接口。

我认为您最好使用set作为Jarekczek说或使用this solution(使用make_heap)。