Skip to content
Work Free practice Coding course Blog Method Results Why me About Enquire Book a call

Coding · Heap (priority queue)

Understanding the problem

Lesson · 8 min read · Heap (priority queue)

The question that keeps being asked

A scheduler holds jobs with priorities. Jobs keep arriving, and whenever a worker is free it must take the most urgent job waiting. There are two obvious designs:

  • An unsorted list. Adding a job is O(1), but finding the most urgent means scanning everything: O(n) per request.
  • A sorted list. The most urgent job is at the front, but every arrival has to be inserted in the right place, shifting what comes after it: O(n) per arrival.

Either way one of the two operations is linear, and over a million arrivals and requests that is around a trillion steps.

StructureAdd a jobTake the most urgent10⁶ of each
Unsorted listO(1)O(n)≈ 5 × 10¹¹ steps
Sorted listO(n)O(1)≈ 5 × 10¹¹ steps
Binary heapO(log n)O(log n)≈ 4 × 10⁷ steps

Half-sorted is enough

A heap keeps just enough order to answer the one question. It is a binary tree, stored in an array, where every parent is no larger than its children. The minimum is always the root, heap[0]. Nothing is promised about siblings or cousins, and that slack is what makes updates cheap.

In the array, the children of index i sit at 2i + 1 and 2i + 2, and its parent at (i − 1) / 2, rounded down. There are no pointers to maintain.

  • Push: put the new value at the end, then swap it with its parent while it is smaller. It rises at most one level per swap, and the tree has log₂ n levels.
  • Pop: take the root, move the last element into its place, then swap it with its smaller child while it is larger. Again at most log₂ n swaps.

Pushes and pops on a min-heap, shown as the array it is stored in. The smallest value is always at index 0.

  1. Step 1 / 8
    heap array
    70
    min7size1

    Push 7 at the end, then let it rise while it is smaller than its parent (no swaps needed).

  2. Step 2 / 8
    heap array
    3071
    min3size2

    Push 3 at the end, then let it rise while it is smaller than its parent: 3↔7.

  3. Step 3 / 8
    heap array
    307192
    min3size3

    Push 9 at the end, then let it rise while it is smaller than its parent (no swaps needed).

  4. Step 4 / 8
    heap array
    10319273
    min1size4

    Push 1 at the end, then let it rise while it is smaller than its parent: 1↔7, 1↔3.

  5. Step 5 / 8
    heap array
    1031927354
    min1size5

    Push 5 at the end, then let it rise while it is smaller than its parent (no swaps needed).

  6. Step 6 / 8
    heap array
    30519273
    popped1size4

    Pop the minimum, 1. The last element, 5, moves to the root and sinks: 5↔3.

  7. Step 7 / 8
    heap array
    3041927354
    min3size5

    Push 4 at the end, then let it rise while it is smaller than its parent: 4↔5.

  8. Step 8 / 8
    heap array
    40519273
    popped3size4

    Pop the minimum, 3. The last element, 5, moves to the root and sinks: 5↔4.

Two patterns that cover most questions

Keep k. To find the k largest of n values, keep a min-heap of size k: a new value that beats the root replaces it. The root is always the k-th largest seen so far. O(n log k) time and O(k) memory, and it works on a stream you cannot store.

Take the best available. When options become available over time, push each as it appears and pop the best whenever you choose. Dijkstra's shortest paths, Huffman codes, merging k sorted lists and most scheduling problems are this loop with a different key.

A heap is not a sorted list. Iterating over the array does not give sorted order; only repeated pops do, and n pops cost O(n log n), which is heapsort.