更新优先队列

Posted

技术标签:

【中文标题】更新优先队列【英文标题】:Updating a PriorityQueue 【发布时间】:2020-04-08 16:23:25 【问题描述】:

当项目是整数与字符串时,PriorityQueue 的不同行为让我非常困惑。但在解决这个问题之前,我想了解以下行为(将项目用作整数)。

假设我有一个包含以下数据的优先级队列(对于每个元素,第一个值是优先级,第二个值是一个项目):

图片1:

执行以下命令后:

pq.put(pq.get())

元素的排序如下:

图 2:

为什么有些元素改变了位置?排序是怎么回事?

这是从调试器中重现这些屏幕截图的代码:

from sys import maxsize
from queue import PriorityQueue

items = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 17, 15, 16]
INFINITY = maxsize
minDist = k: INFINITY for k in items
minDist[3] = 0

pq = PriorityQueue(len(minDist))

for v in minDist.keys():
    pq.put([minDist[v], v])

# At this point, pq has the elements as shown in Image 1

pq.put(pq.get())

# Now, pq has elements scrambled as shown in Image 2

经验教训

根据下面的调查和讨论,每次调用pq.put(pq.get()) 都会重新排列优先级队列数据结构。目前尚不清楚是否有办法控制它。如果你知道怎么做,请在下面评论!

我的具体问题似乎与物品的排序有关。如图 1 和 2 所示,这些项目是从 1 到 17 的整数。如果将它们转换为字符串(即"1""2"、...、"17"),我会得到不同的行为来自我原始代码中的主要功能。但是,如果我对每个键使用zfill(2),这样我就有"01""02"、...、"17",我可以获得与使用整数相同的最终结果而是。

【问题讨论】:

我无法分享原始代码,希望这些图像足以补充我的问题。我会根据您的要求添加一些简单的代码。 更新了示例代码。 【参考方案1】:

我不清楚您显示的屏幕截图中发生了什么,但下面显示了一个与解释器的示例会话,它试图用稍微不同的方法重现问题:

Python 3.8.2 (tags/v3.8.2:7b3ab59, Feb 25 2020, 23:03:10) [MSC v.1916 64 bit (AMD64)] on win32
Type "help", "copyright", "credits" or "license()" for more information.
>>> import math, pprint, queue
>>> min_dist = x: math.inf for x in range(1, 17)
>>> min_dist[3] = 0
>>> pq = queue.PriorityQueue(len(min_dist))
>>> for key, value in min_dist.items():
    pq.put((value, key))


>>> pq_items = []
>>> while not pq.empty():
    pq_items.append(pq.get())


>>> pprint.pprint(pq_items)
[(0, 3),
 (inf, 1),
 (inf, 2),
 (inf, 4),
 (inf, 5),
 (inf, 6),
 (inf, 7),
 (inf, 8),
 (inf, 9),
 (inf, 10),
 (inf, 11),
 (inf, 12),
 (inf, 13),
 (inf, 14),
 (inf, 15),
 (inf, 16)]
>>> for key, value in min_dist.items():
    pq.put((value, key))


>>> pq.put(pq.get())
>>> pq_items.clear()
>>> while not pq.empty():
    pq_items.append(pq.get())


>>> pprint.pprint(pq_items)
[(0, 3),
 (inf, 1),
 (inf, 2),
 (inf, 4),
 (inf, 5),
 (inf, 6),
 (inf, 7),
 (inf, 8),
 (inf, 9),
 (inf, 10),
 (inf, 11),
 (inf, 12),
 (inf, 13),
 (inf, 14),
 (inf, 15),
 (inf, 16)]
>>> 

您可能希望调整您的代码以使用类似的方法来获得您正在寻找的内容。如果您在使用字符串时遇到问题,那么您可能需要检查将元素放入列表然后排序后的排序方式。

【讨论】:

谢谢!在对您的代码进行了一些小改动之后(我定义了items = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 17, 15, 16] 来替换range(1, 17) 只是为了让它与我的完全匹配,如果我在命令pq_items.clear() 处添加一个断点,即在命令pq.put(pq.get()) 之后, pq 中的数据正是我在图 2 中显示的内容。顺序被打乱了。显然,对于每个 pq.get(),项目都会被打乱。如果它们都具有相同的优先级,它们将重新排列在 "不可预测”的方式。 只需使用调试器并逐行检查每次pq.get() 调用后pq 中的数据会发生什么情况。即使优先级相同,元素也会重新排列。 @Bruno 如果您查看pq.queue,那么您是对的。底层数据结构中元素的顺序可以改变顺序。但是,如我的回答所示,从PriorityQueue 实例中出来的元素的有效顺序是相同的。 是的,现在才意识到。感谢您的见解。我改编了geekviewpoint.com/python/graph/dijkstra_constrained 中的代码,以便minDist 是字典字典而不是列表列表。其动机是允许使用非常通用的密钥(intstr 等)。我面临的并且影响了这个问题的问题是,当我将键作为特定示例的字符串时,修改后的代码与键是整数的工作方式不同。这真让我抓狂!无论我使用字符串还是整数,一个较小的示例都可以正常工作。 查看上面链接中的函数updatePriorityQueue。这就是我询问pq.put(pq.get()) 的原因。因为每次调用此类函数后项目都会以不同的方式重新排列,如果它们是整数与字符串,则仅因为键类型而改变主函数的行为,即使关联的值相同。

以上是关于更新优先队列的主要内容,如果未能解决你的问题,请参考以下文章

优先级队列实现为单链接未能在插入时更新指针引用

POJ2431(优先队列)

优先队列与TopK

在优先级队列中分配随机优先级?

优先队列 + 并查集 + 字典树 + 树状数组 + 线段树 + 线段树点更新 + KMP +AC自动机 + 扫描线

优先队列下的迪杰斯特拉算法(结构体与队列的冲突)