Priority queue with a heap
Insert into a max-heap by sifting the new item up, and extract the maximum by sifting the item placed at the root down; both O(log n).
A priority queue always hands out the largest item first. With a max-heap (a complete tree where no node is
smaller than its children, stored in an array) both operations take O(log n):
- Time:
O(log n) per operation - Space:
O(1) extra - CLRS, section 6.5
- Horowitz–Sahni, chapter 5
A ten-item heap; key 15 rises to one level below the root, then the maximum (16) is extracted.
def priority_queue(heap, key):insert(heap, key)top = extract_max(heap)return top, heapdef insert(heap, key):heap.append(key)i = len(heap) - 1while i > 0:parent = (i - 1) // 2if heap[parent] >= heap[i]:breakheap[i], heap[parent] = heap[parent], heap[i]i = parentdef extract_max(heap):top = heap[0]heap[0] = heap[-1]heap.pop()i = 0size = len(heap)while True:largest = ileft = 2 * i + 1right = 2 * i + 2if left < size:if heap[left] > heap[largest]:largest = leftif right < size:if heap[right] > heap[largest]:largest = rightif largest == i:return topheap[i], heap[largest] = heap[largest], heap[i]i = largest
Start
Local variables
Orange = changed in this step
heap list
Call stack
- priority_queue()line 1
How the work grows with input size
Average of 12 random graphs per n (fixed seeds). Hollow marker = the input on screen.
- comparisons
- swaps
- current input (n=10)
Show as a table
| n | comparisons | swaps |
|---|---|---|
| 4 | 3.8 | 0.9 |
| 6 | 5.2 | 1.8 |
| 8 | 6.1 | 2.2 |
| 10 | 7.2 | 2.8 |
| 12 | 7.6 | 3.3 |
| 14 | 7.3 | 3.3 |
Explanation
The idea
A priority queue always hands out the largest item first. With a max-heap (a complete tree where no node is
smaller than its children, stored in an array) both operations take O(log n):
- Insert: put the key at the end of the heap and, while it is larger than its parent, swap it with the parent (sift up).
- Extract the maximum: take the root, put the last item at the root, and swap it with its larger child until it is not smaller than either child (sift down).
Cost
The heap is ⌊log n⌋ high and each operation walks at most one root-to-leaf path: O(log n). This is the queue that
Dijkstra, Prim and job schedulers use.
What to watch in the trace
- The tree above the bars is the same array drawn as a heap.
- Follow the new key on its way up, and the replacement root on its way down.