AlgoVerse

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):

Read the full explanation ↓
Input:
n=10 · target=15PDF export

A ten-item heap; key 15 rises to one level below the root, then the maximum (16) is extracted.

  1. def priority_queue(heap, key):
  2. insert(heap, key)
  3. top = extract_max(heap)
  4. return top, heap
  5. def insert(heap, key):
  6. heap.append(key)
  7. i = len(heap) - 1
  8. while i > 0:
  9. parent = (i - 1) // 2
  10. if heap[parent] >= heap[i]:
  11. break
  12. heap[i], heap[parent] = heap[parent], heap[i]
  13. i = parent
  14. def extract_max(heap):
  15. top = heap[0]
  16. heap[0] = heap[-1]
  17. heap.pop()
  18. i = 0
  19. size = len(heap)
  20. while True:
  21. largest = i
  22. left = 2 * i + 1
  23. right = 2 * i + 2
  24. if left < size:
  25. if heap[left] > heap[largest]:
  26. largest = left
  27. if right < size:
  28. if heap[right] > heap[largest]:
  29. largest = right
  30. if largest == i:
  31. return top
  32. heap[i], heap[largest] = heap[largest], heap[i]
  33. i = largest

Invariant: Apart from the one place being repaired, no node is larger than its parent.

Start

1614108793241target 1516014110283749536274819

Local variables

key15

Orange = changed in this step

heap list

1614108793241

Call stack

  1. priority_queue()line 1
Step 1 of 61
Black = inserted, extracted · Orange = key stepcompare: 0/8 · swap: 0/4 · append: 0/1 · inserted: 0/1 · move_last: 0/1 · extracted: 0/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)
    02468468101214n = number of itemscomparisonsswaps
    Show as a table
    ncomparisonsswaps
    43.80.9
    65.21.8
    86.12.2
    107.22.8
    127.63.3
    147.33.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.