awesome-everything RU
↑ Back to the climb

Algorithms from zero

Heaps: code reading

Crux Read real heap code and predict behaviour: a sift-up bug, the build-heap loop bound, the top-k size-k heap, and the heapify complexity argument.
Your altitude — climbing toward senior
ZeroJuniorMiddleSenior
You are at middle altitude — in the sky
◷ 14 min

Heap bugs hide in index arithmetic and loop bounds — a sign flip, an off-by-one in the last non-leaf, a min/max mix-up. Read each snippet the way you would in review: trace the invariant, then pick the precise defect or the correct claim.

Goal

Practise reading heap code at the level a senior engineer reviews it: spot a sift-up that compares the wrong way, justify the build-heap loop bound, confirm the top-k heap is sized and oriented correctly, and defend why heapify is O(n).

Snippet 1 — sift-up

// Intended: min-heap. siftUp restores the property after a push.
function siftUp(heap, i) {
  while (i > 0) {
    const parent = Math.floor((i - 1) / 2);
    if (heap[i] > heap[parent]) {        // <-- comparison
      [heap[i], heap[parent]] = [heap[parent], heap[i]];
      i = parent;
    } else {
      break;
    }
  }
}
Quiz

This is meant to maintain a MIN-heap, but the property breaks after pushes. What is the defect?

Snippet 2 — build-heap

function buildHeap(arr) {           // sift-down each non-leaf, bottom-up
  const start = Math.floor(arr.length / 2) - 1;
  for (let i = start; i >= 0; i--) {
    siftDown(arr, i);
  }
  return arr;
}
Quiz

Why does the loop start at Math.floor(n/2) - 1 and go downward, and what would break if it started at 0 and went upward instead?

Snippet 3 — top-k

function topKLargest(nums, k) {
  const h = new MinHeap();              // min-heap, root is smallest
  for (const x of nums) {
    h.push(x);
    if (h.size() > k) h.pop();          // drop the current minimum
  }
  return h.toArray();                   // the k largest (heap order)
}
Quiz

For nums = [4, 1, 7, 3, 8, 5] and k = 3, what does the heap hold at the end, and what is the running time?

Snippet 4 — heapify complexity

A heap built bottom-up calls sift-down once per non-leaf.
Node counts by height h (n total):  ~n/2 at h=0, n/4 at h=1, n/8 at h=2, ...
sift-down cost from a node of height h is O(h).
Total work  T(n) = Σ_h (n / 2^(h+1)) · h
Quiz

Evaluating the sum T(n) = Σ_h (n / 2^(h+1)) · h, what is build-heap's complexity, and why is the intuition 'n/2 sift-downs × O(log n) each = O(n log n)' wrong?

Recap

Heap correctness lives in details you read line by line: sift-up’s comparison must match min vs max (child < parent for a min-heap); build-heap must run bottom-up from the last non-leaf at floor(n/2)-1 so each node’s subtrees are already heaps; top-k uses a size-k MIN-heap and pops the root on overflow, leaving the k largest in O(n log k); and heapify is O(n) because the height-weighted node sum Σ h/2^h converges — most nodes are cheap leaves. Trace the invariant before you trust the code.

Continue the climb ↑Heaps: build a priority queue and merge k streams
shortcuts expand
search
K
prev piece
k
next piece
j
cycle tier
t
this menu
?
sources3
expand
  1. 01
  2. 02
  3. 03

Trademarks belong to their respective owners. Editorial reference only.