1. 程式人生 > 其它 >1.5 實現一個優先順序佇列

1.5 實現一個優先順序佇列

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

問題描述

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

解決方案

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

import heapq


class PriorityQueue:
    """
    簡單的優先順序佇列類
    """
    def __init__(self):
        self._queue = []
        self._index = 0

    def push(self, item, priority: int):
        """
        在佇列中加入新的值,並保持堆的不變性(即保持最小堆)

        :param item: 要加入的值
        :param priority: 優先順序
        :return: None
        """
        heapq.heappush(self._queue, (-priority, self._index, item))
        self._index += 1

    def pop(self):
        """
        彈出self._queue[0][-1],即優先順序最高的元素的值

        :return: item
        """
        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('happy'), 1)
q.push(Item('good'), 5)
q.push(Item('beautiful'), 3)
q.push(Item('nice'), 1)

for i in range(0, 4):
    print(q.pop())
"""
輸出結果:
Item('good')
Item('beautiful')
Item('happy')
Item('nice')
"""

討論

這裡的底層實現是堆排序。函式heapq.heapqpush()heapq.heappop()分別在佇列_queue上插入和刪除第一個元素,並且因為_queue是最小堆,所以-priority最小的元組總是排在第一個。這樣就實現了按優先順序返回元素。

在上面的程式碼中,佇列中的每個元素是一個元組(-priority, index, item)。元組的大小比較是從第一個元素開始比較,比較結果即為元組比較結果,如相等則依次向後比較。index變數的作用是保證同優先順序的元素按照它們的插入順序排序和比較。
我們設定Item例項是不支援排序的:

a = Item('happy')
b = Item('good')
a < b
"""
執行結果:
TypeError: '<' not supported between instances of 'Item' and 'Item'
"""

如果使用元組(-priority, item),那麼當優先順序相同時,就會出現如上的執行錯誤。
通過引入另外的index變數組成三元組(-priority, index, item),就能很好的避免此錯誤,因為不可能有相同的index值。

關於heapq模組的更多詳細說明可以參考官方文件