Priority Queue
Remarks
- Priority Queue is a Queue, in which elements will be dequeued based on their priorites(other than timestamp).
- Elements on the queue should be comparable, otherwise we can’t deduce their ralative priorities.
- Priority Queue can be implemented with heap.
- Heap is a tree in which the parent is always larger(max heap) or smaller(min heap) than its children. (Heap property)
- We can implement priority queue using complete binary heap.
Time complexity of a Binary Heap Queue
| operation | time | 
|---|---|
| construct | O(N) | 
| front | O(1) | 
| dequeue | O(logN) | 
| enqueue | O(logN) | 
Implementation
from operator import lt, gt
class HeapQueue:
    comparators = {'min': lt, 'max': gt}
    def __init__(self, data=None, comparator='min'):
        self._heapq = [None] + list(data) if data else [None]
        self._comparator = self.comparators.get(comparator, comparator)
        self._n = len(self._heapq) - 1
        if data: self.heapify()
    def _compare(self, i, j):
        return self._comparator(self._heapq[i], self._heapq[j])
    def __len__(self):
        return self._n
    def is_empty(self):
        return self._n == 0
    def _swap(self, i, j):
        self._heapq[i], self._heapq[j] = self._heapq[j], self._heapq[i]
    def front(self):
        assert not self.is_empty(), 'heapq is empty'
        return self._heapq[1]
    def enqueue(self, x):
        self._heapq.append(x)
        self._n += 1
        self.swim(self._n)
    def dequeue(self):
        assert not self.is_empty(), 'heapq is empty'
        self._swap(1, self._n)
        self._n -= 1
        self.sink(1)
        return self._heapq.pop()
    def swim(self, k):
        while k > 1 and self._compare(k, k >> 1):
            self._swap(k, k >> 1); k >>= 1
    def sink(self, k):
        n = self._n
        while k * 2 <= n:
            j = k * 2
            if j < n and self._compare(j + 1, j): j += 1
            if self._compare(k, j): break
            self._swap(k, j); k = j
    def heapify(self):
        for k in range(self._n >> 1, 0, -1):
            self.sink(k)
Simple wrapper over heapq for min priority queue.
from heapq import heapify, heappush, heappop
class PriorityQueue:
    def __init__(self, data=[]): 
        self.data = list(data)
        heapify(self.data)
    def put(self, x): heappush(self.data, x)
    def get(self): return heappop(self.data)
    def top(self): return self.data[0]
From python documentation, how to skip the comparison with item
from dataclasses import dataclass, field
from typing import Any
@dataclass(order=True)
class PrioritizedItem:
    priority: int
    item: Any = field(compare=False)
class Dummy:
    def __init__(self, val): self.val = val
items = [Dummy('b'), Dummy(None), Dummy(Exception('abc'))]
priorities = [1,2,1]
pq = PriorityQueue()
for p, i in zip(priorities, items):
    pq.put(PrioritizedItem(priority=p, item=i))
for _ in range(3):
    print(pq.get().item.val)
# gets b, abc, None
Add order to priority.
pq = PriorityQueue()
for o, (p, i) in enumerate(zip(priorities, items)):
    pq.put(PrioritizedItem(priority=(p, -o), item=i))
for _ in range(3):
    print(pq.get().item.val)
# gets abc, b, None
Hack the prioritized item to be used as max priority queue
@dataclass(order=False)
class PrioritizedItem:
    priority: int
    item: Any = field(compare=False)
    def __lt__(self, other): return self.priority > other.priority
pq = PriorityQueue()
for o, (p, i) in enumerate(zip(priorities, items)):
    pq.put(PrioritizedItem(priority=(p, -o), item=i))
for _ in range(3):
    print(pq.get().item.val)
# gets None, b, abc
pq = PriorityQueue()
for o, (p, i) in enumerate(zip(priorities, items)):
    pq.put(PrioritizedItem(priority=(p, o), item=i))
for _ in range(3):
    print(pq.get().item.val)
# gets None, abc, b