1. 程式人生 > 程式設計 >Python實現一個優先順序佇列的方法

Python實現一個優先順序佇列的方法

問題

怎樣實現一個按優先順序排序的佇列? 並且在這個佇列上面每次 pop 操作總是返回優先順序最高的那個元素

解決方案

下面的類利用 heapq 模組實現了一個簡單的優先順序佇列:

import heapq

class PriorityQueue:
  def __init__(self):
    self._queue = []
    self._index = 0

  def push(self,item,priority):
    heapq.heappush(self._queue,(-priority,self._index,item))
    self._index += 1

  def pop(self):
    return heapq.heappop(self._queue)[-1]

下面是它的使用方式:

>>> class Item:
...   def __init__(self,name):
...     self.name = name
...   def __repr__(self):
...     return 'Item({!r})'.format(self.name)
...
>>> q = PriorityQueue()
>>> q.push(Item('foo'),1)
>>> q.push(Item('bar'),5)
>>> q.push(Item('spam'),4)
>>> q.push(Item('grok'),1)
>>> q.pop()
Item('bar')
>>> q.pop()
Item('spam')
>>> q.pop()
Item('foo')
>>> q.pop()
Item('grok')
>>>

仔細觀察可以發現,第一個 pop() 操作返回優先順序最高的元素。 另外注意到如果兩個有著相同優先順序的元素( foogrok ),pop 操作按照它們被插入到佇列的順序返回的。

討論

這一小節我們主要關注 heapq 模組的使用。 函式 heapq.heappush() heapq.heappop() 分別在佇列 _queue 上插入和刪除第一個元素, 並且佇列 _queue 保證第一個元素擁有最高優先順序( 1.4 節已經討論過這個問題)。 heappop() 函式總是返回”最小的”的元素,這就是保證佇列pop操作返回正確元素的關鍵。 另外,由於 push 和 pop 操作時間複雜度為 O(log N),其中 N 是堆的大小,因此就算是 N 很大的時候它們執行速度也依舊很快。

在上面程式碼中,佇列包含了一個 (-priority,index,item) 的元組。 優先順序為負數的目的是使得元素按照優先順序從高到低排序。 這個跟普通的按優先順序從低到高排序的堆排序恰巧相反。

index 變數的作用是保證同等優先順序元素的正確排序。 通過儲存一個不斷增加的 index 下標變數,可以確保元素按照它們插入的順序排序。 而且, index 變數也在相同優先順序元素比較的時候起到重要作用。

為了闡明這些,先假定 Item 例項是不支援排序的:

>>> a = Item('foo')
>>> b = Item('bar')
>>> a < b
Traceback (most recent call last):
File "<stdin>",line 1,in <module>
TypeError: unorderable types: Item() < Item()
>>>

如果你使用元組 (priority,item) ,只要兩個元素的優先順序不同就能比較。 但是如果兩個元素優先順序一樣的話,那麼比較操作就會跟之前一樣出錯:

>>> a = (1,Item('foo'))
>>> b = (5,Item('bar'))
>>> a < b
True
>>> c = (1,Item('grok'))
>>> a < c
Traceback (most recent call last):
File "<stdin>",in <module>
TypeError: unorderable types: Item() < Item()
>>>

通過引入另外的 index 變數組成三元組 (priority,item) ,就能很好的避免上面的錯誤, 因為不可能有兩個元素有相同的 index 值。Python 在做元組比較時候,如果前面的比較已經可以確定結果了, 後面的比較操作就不會發生了:

>>> a = (1,1,Item('bar'))
>>> c = (1,2,Item('grok'))
>>> a < b
True
>>> a < c
True
>>>

如果你想在多個執行緒中使用同一個佇列,那麼你需要增加適當的鎖和訊號量機制。 可以檢視 12.3 小節的例子演示是怎樣做的。

heapq 模組的官方文件有更詳細的例子程式以及對於堆理論及其實現的詳細說明。

以上就是Python實現一個優先順序佇列的方法的詳細內容,更多關於Python實現優先順序佇列的資料請關注我們其它相關文章!