如何在Python中实现优先级队列?

时间:2012-04-01 23:22:19

标签: python queue heap priority-queue

很抱歉这个愚蠢的问题,但Python文档令人困惑。

链接1:队列实施 http://docs.python.org/library/queue.html

它表示该队列具有优先级队列的构造。但我找不到如何实现它。

class Queue.PriorityQueue(maxsize=0)

链接2:堆实施 http://docs.python.org/library/heapq.html

他们说我们可以使用heapq

间接实现优先级队列
pq = []                         # list of entries arranged in a heap
entry_finder = {}               # mapping of tasks to entries
REMOVED = '<removed-task>'      # placeholder for a removed task
counter = itertools.count()     # unique sequence count

def add_task(task, priority=0):
    'Add a new task or update the priority of an existing task'
    if task in entry_finder:
        remove_task(task)
    count = next(counter)
    entry = [priority, count, task]
    entry_finder[task] = entry
    heappush(pq, entry)

def remove_task(task):
    'Mark an existing task as REMOVED.  Raise KeyError if not found.'
    entry = entry_finder.pop(task)
    entry[-1] = REMOVED

def pop_task():
    'Remove and return the lowest priority task. Raise KeyError if empty.'
    while pq:
        priority, count, task = heappop(pq)
        if task is not REMOVED:
            del entry_finder[task]
            return task
    raise KeyError('pop from an empty priority queue'

哪个是python中最有效的优先级队列实现?以及如何实现它?

3 个答案:

答案 0 :(得分:28)

没有&#34;最有效的优先级队列实现&#34;在任何语言

优先级队列都是权衡取舍。见http://en.wikipedia.org/wiki/Priority_queue

您应根据计划使用方式选择其中一种:

  • O(log(N))插入时间和O(1) findMin + deleteMin时间,或
  • O(1)插入时间和O(log(N)) findMin + deleteMin time

在后一种情况下,您可以选择使用Fibonacci堆实现优先级队列:http://en.wikipedia.org/wiki/Heap_(data_structure)#Comparison_of_theoretic_bounds_for_variants(如您所见,heapq基本上是二叉树,必须具有{{1}对于插入和findMin + deleteMin)

如果您正在处理具有特殊属性(例如有界数据)的数据,那么您可以实现O(log(N))插入和O(1) findMin + deleteMin时间。您只能对某些类型的数据执行此操作,否则您可能会滥用您的优先级队列以违反排序中的O(1)限制。

要使用任何语言实现任何队列,您只需要定义O(N log(N))insert(value)操作。这通常只涉及底层堆的最小包装;请参阅http://en.wikipedia.org/wiki/Fibonacci_heap以实现您自己的,或使用类似堆的现成库,如配对堆(Google搜索显示http://svn.python.org/projects/sandbox/trunk/collections/pairing_heap.py


如果您只关心您引用的两个中的哪一个效率更高(上面包含的基于extractMin() -> value的{​​{3}}代码,而不是heapq),那么:

在网络上似乎没有任何关于Queue.PriorityQueue实际在做什么的容易找到的讨论;您需要深入了解代码,该代码链接到帮助文档:http://docs.python.org/library/heapq.html#priority-queue-implementation-notes

Queue.PriorityQueue

我们可以看到, 224 def _put(self, item, heappush=heapq.heappush): 225 heappush(self.queue, item) 226 227 def _get(self, heappop=heapq.heappop): 228 return heappop(self.queue) 也使用Queue.PriorityQueue作为底层机制。因此它们同样糟糕(渐近地说)。 heapq可能允许并行查询,所以我会打赌它可能会有一个非常小的常数因子更多的开销。但是因为你知道底层实现(和渐近行为)必须是相同的,最简单的方法就是在同一个大型数据集上运行它们。

(请注意,Queue.PriorityQueue似乎没有办法删除条目,而Queue.PriorityQueue则这样做。但这是一把双刃剑:优先级高的队列实现可能允许您删除在O(1)或O(log(N))时间内的元素,但是如果你使用你提到的heapq函数,并让那些僵尸任务在你的队列中累积,因为你没有从它们中提取它们min,然后你会看到渐渐减速,你不会看到它。当然,你首先不能用remove_task来做这个,所以这里不能进行比较。)< / p>

答案 1 :(得分:27)

Queue 模块中的版本使用 heapq 模块implemented,因此它们对底层堆操作具有相同的效率。

也就是说, Queue 版本较慢,因为它增加了锁,封装和一个很好的面向对象的API。

priority queue suggestions shown in the heapq docs旨在说明如何向优先级队列添加其他功能(例如排序稳定性和更改先前排队任务的优先级的能力)。如果您不需要这些功能,那么基本的 heappush heappop 功能将为您提供最快的性能。

答案 2 :(得分:1)

虽然这个问题已被回答并被标记为已接受,但仍然是一个简单的自定义优先级队列实现,而不使用任何模块来理解它是如何工作的。

# class for Node with data and priority
class Node:

  def __init__(self, info, priority):
    self.info = info
    self.priority = priority

# class for Priority queue 
class PriorityQueue:

  def __init__(self):
    self.queue = list()
    # if you want you can set a maximum size for the queue

  def insert(self, node):
    # if queue is empty
    if self.size() == 0:
      # add the new node
      self.queue.append(node)
    else:
      # traverse the queue to find the right place for new node
      for x in range(0, self.size()):
        # if the priority of new node is greater
        if node.priority >= self.queue[x].priority:
          # if we have traversed the complete queue
          if x == (self.size()-1):
            # add new node at the end
            self.queue.insert(x+1, node)
          else:
            continue
        else:
          self.queue.insert(x, node)
          return True

  def delete(self):
    # remove the first node from the queue
    return self.queue.pop(0)

  def show(self):
    for x in self.queue:
      print str(x.info)+" - "+str(x.priority)

  def size(self):
    return len(self.queue)

在此处查找完整的代码和说明:https://www.studytonight.com/code/python/ds/priority-queue-in-python.php