您如何更新priority_queue中的值,或者是不是有另一种方法可以在c ++中更新堆中的键

Posted

技术标签:

【中文标题】您如何更新priority_queue中的值,或者是不是有另一种方法可以在c ++中更新堆中的键【英文标题】:How do you update values in priority_queue, or is there another way to update keys in heaps in c++您如何更新priority_queue中的值,或者是否有另一种方法可以在c ++中更新堆中的键 【发布时间】:2020-03-05 04:48:59 【问题描述】:

当我注意到 Djikstra 的算法时,我可以在 O(logn) 时间内更新堆中的键(使用 n 个键)(pseudocode 中的最后一行)。如何在 C++ 中更新堆中的键,priority_queues 中是否有任何方法可以做到这一点?还是我必须编写自己的堆类才能像这样在 O(logn) 中实现更新?

编辑 1: 澄清我的需求 - 对于具有 n 个元素的二进制堆 - 1) 应插入新值并在 O(logn) 中查找并弹出最小值 2) 应该更新 O(logn) 中已经存在的键

我尝试想出一种方法来使用 make_heap、push_heap、pop_heap 和 John Ding 建议的用于更新的自定义函数来实现这一点。

但是我在制作函数时遇到了问题,我首先需要在堆中找到键的位置。在堆中的 O(logn) 下执行此操作需要查找数组以查找堆中键的位置,see here(我不知道任何其他方式)。但是,当我调用 push_heap 或 pop_heap 时,这些查找表不会更新。

【问题讨论】:

您实际上是在问“如何更改 priority_queue 中已有元素的优先级?”或者更抽象地说“如果我需要更改队列中元素的优先级,我应该使用哪个 C++ 容器?” 对,我如何在 O(logn) 时间内为具有 n 个元素的 priority_queue 执行此操作?如果不可能,您如何建议我以不同的方式进行。我希望更新 priority_queue 中已经存在的元素的键。 您需要对此进行基准测试,以了解它在 您的 特定用例中的工作方式,但一些排序算法在几乎排序的列表上运行得非常快,所以如果您是只需更改少数条目,您就可以使用 list 并根据任何更改对其进行排序。 您也可以remove the element 并重新注入它,或者花哨并创建一个reemplace() 函数来为您执行此操作。 在几乎排序的列表中排序至少需要 O(n) 时间,对吗?您需要将其移回原位。 【参考方案1】:

您可以使用 priority_queue 优化 dijktra 算法。它由二进制堆实现,您可以在O(logN) 时间内弹出顶部或推入元素。但是由于priority_queue的封装,你不能修改任何元素的key(更准确地说是减少key)。

所以我们的方法是将多个元素推入堆中,不管我们是否有多个元素指向同一个节点。

例如,当

Node N : distance = 30, GraphNode = A(其中A表示图中的一个节点,N表示堆中的一个节点)

已经在堆中,那么当我们尝试放松节点N时,使用priority_queue无法帮你做这样的操作:

decrease_key_to(N, 20)

通过减少键可以使堆总是包含少于N个元素,但priority_queue无法实现

我们可以用它做的是在堆中添加另一个节点:

Node N2 : distance = 20, GraphNode = A
push N2 into the heap

对应priority_queue::push

所以你可能需要自己实现一个支持decrease_key的二叉堆,或者在网上找一个实现,并存储一个指向堆中每个元素的指针表,以便通过图中的节点知道访问元素。

作为扩展,使用斐波那契堆甚至可以让decrease_key更快,这就是Dijkstra的极致,哈哈:)


我的回答的上一个版本的问题:

我们无法定位使用push_heap 推入堆的元素。

【讨论】:

约翰,我目前的 djikstra 实现是你在这里提到的第一个。您能否补充一下您将如何使用 make_heap、push_heap 和 pop_heap 来实现 reduce_key(x, new_val)。我们会不会因为 push_heap 和 pop_heap 只在数组末尾起作用而被卡住。 我不知道斐波那契堆,我会检查一下。 Djikstra 的复杂程度如何? 我想你的意思是,我们可以实现其他人已经存在的 decrese_key 方法,对吧。谢谢。 @NUMBART 可以简单的在 Github 上搜索基于堆的数组或者什么的,只需要复制decrease_key 函数并使用标准库来构建、推送、弹出堆。如果我没记错的话,我估计使用priority_queue、二叉堆和decrease_key、斐波那契堆的复杂度分别是O(ElgE)O(ElgN)O(NlgN) amortized @NUMBART 我没有看到你的问题。对不起,“我们不会因为 push_heap 和 pop_heap 只在数组末尾工作而被卡住吗?”。不。decrese_key 不需要堆是基于节点的。【参考方案2】:

为了做到这一点,您需要的不仅仅是priority_queue 提供的:您需要知道要更新的元素在底层容器中的何处。例如,在二叉堆中,您需要知道要更改其优先级的元素的位置。有了这个,您可以更改元素的优先级,然后通过向上或向下冒泡修改的元素来恢复 O(log n) 中的堆属性。

对于 Dijkstra,如果我没记错的话,称为斐波那契堆的东西比二叉堆更有效。

【讨论】:

我一定会检查斐波那契堆,正如两个答案所暗示的那样。此外,tadman 在remove element 上分享了此链接。我对实现并不完全确定,但是您认为可以在 log(n) 中将其删除然后重新插入。 对不起,我也不确定。一般来说,您绝对可以在 O(log(n)) 时间内实现删除,但我不清楚建议的实现是否这样做。【参考方案3】:

不幸的是,std::priority_queue 不支持更新堆中的条目,但您可以使堆中的条目无效并等待它们渗透到最终可以删除的根。因此,您无需更改现有条目,而是将其无效并插入另一个具有新优先级的条目。您是否可以忍受无效条目填满堆的后果,由您来判断。

有关这在实践中如何运作的想法,请参阅here。

【讨论】:

以上是关于您如何更新priority_queue中的值,或者是不是有另一种方法可以在c ++中更新堆中的键的主要内容,如果未能解决你的问题,请参考以下文章

STL容器 -- Priority_Queue

您如何以编程方式更新 Qt 中的 UI?

用另一个表上的值更新新添加的列

如何在可重用的 tableView 单元格中更新 UITextfield 中的文本值,每个单元格中的值不同

Realm android中的更新语句

Day1 Heap与Priority_queue