【问题标题】:How to iterate over a Priority Queue?如何迭代优先队列?
【发布时间】:2014-09-13 13:57:04
【问题描述】:

我正在尝试在 Python 中实现 Uniform-cost search,为此,我需要一个优先级队列,我正在使用标准库中的 queue.py。但是,算法的最后会检查队列中是否有任何成本更高的路径。如果它不可迭代,我如何检查我的队列中是否有任何给定值?

【问题讨论】:

  • 我建议使用简单的listheapq 编写自己的优先级队列。没那么糟糕。请参阅 heapq 文档中的 implementation notes
  • @roippi 这正是queue.PriorityQueue 的实现方式:)

标签: python search


【解决方案1】:

queue.PriorityQueue 实际上是使用list 实现的,put/get 方法使用heapq.heappush/heapq.heappop 来维持list 内部的优先顺序。因此,如果您愿意,您可以遍历内部列表,该列表包含在 queue 属性中:

>>> from queue import PriorityQueue
>>> q = PriorityQueue()
>>> q.put((5, "a"))
>>> q.put((3, "b"))
>>> q.put((25, "c"))
>>> q.put((2, "d"))
>>> print(q.queue)
[(2, 'd'), (3, 'b'), (25, 'c'), (5, 'a')]

【讨论】:

  • 第一行应该是:from Queue import PriorityQueue。注意大写 Q。
  • @SushilVerma 是的,对于 Python 2。在 Python 3.x 中,它是小写的 q。
  • 正如我的好朋友 T. Payne 在他的一个视频中展示的那样: ` try: import queue as Queue except: import Queue 两者都适用
【解决方案2】:

PriorityQueue 实现为binary heap,在python 中使用list(数组)实现。要遍历队列,您需要了解有关子项在列表中的存储位置的规则。

规则是所有节点都有两个孩子,除非它们是最后一个有孩子的节点,在这种情况下他们可能有一个孩子。出现在最后一个有子节点之后的所有节点都将有零个子节点(duh)。

节点的子节点相对于节点在列表中的位置进行存储。其中i 是列表中节点的索引,那么它的子节点存储在:

  • 2 * i + 1
  • 2 * i + 2

但是,堆的唯一要求是节点的所有子节点的值都大于或等于节点的值(或大于取决于实现)。

例如,在上面链接的关于二进制堆的 wiki 页面中,您会找到以下图像。队列中的第一项是根。很明显。第二项是根的孩子中较大的一个。但是,第三项可以是根节点的剩余节点,也可以是队列中第二个节点的任何一个子节点。也就是说,队列中的第三个项目 (25) 可能与 19 或 1 处于相同位置。

因此,要遍历队列,您需要跟踪所有当前“可查看”的节点。例如:

def iter_priority_queue(queue):

    if queue.empty():
        return

    q = queue.queue
    next_indices = [0]
    while next_indices:
        min_index = min(next_indices, key=q.__getitem__)
        yield q[min_index]
        next_indices.remove(mix_index)
        if 2 * min_index + 1 < len(q):
            next_indices.append(2 * min_index + 1)
        if 2 * min_index + 2 < len(q):
            next_indices.append(2 * min_index + 2)

如果您觉得懒惰,该方法可以猴子修补到queue.PriorityQueue,但我鼓励您使用heapq 模块实现自己的优先队列类,因为PriorityQueue 带有很多多余的功能(主要是线程安全的,几乎可以肯定不需要)。需要注意的是,上述方法不是线程安全的。如果另一个线程在迭代时修改了队列,那么上述方法将开始产生错误的数字,如果幸运的话,它可能会产生异常。

【讨论】:

    【解决方案3】:

    除非您需要queue.queue 提供的线程安全,因为它主要用作线程安全作业队列而不是通用队列,否则您最好直接使用collections.deque,因为它们是可迭代的。

    【讨论】:

    • 但如何将其用作 PriorityQueue?
    猜你喜欢
    • 1970-01-01
    • 2012-11-25
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-03-24
    • 2019-08-11
    • 1970-01-01
    • 2017-04-11
    相关资源
    最近更新 更多