Complete trees in arrays with parents ≤ children: O(1) minimum, O(log n) push/pop, O(n) heapify — and the top-k / k-way-merge / running-median patterns.
0 of 8 lessons checked off
Introduction
What it is
A binary heap is a COMPLETE binary tree (levels fill left to right) obeying one local rule: in a min-heap every parent ≤ its children. The global consequence: the minimum sits at the root.
Completeness means no pointers are needed — the tree lives in an array where node i's children are at 2i+1 and 2i+2 and its parent at (i−1)//2.
Why it matters
A heap is the answer to 'I repeatedly need the smallest/largest thing': O(1) peek, O(log n) insert and extract — the engine of priority queues.
It powers a pattern family interviews adore: top-k elements, k-th largest, merge k sorted lists, running median, task scheduling by deadline.
How it works
Insert: append at the end (keeping completeness), then sift UP while smaller than the parent.
Extract-min: take the root, move the LAST element to the root, sift DOWN swapping with the smaller child.
Heapify an entire array bottom-up in O(n) — cheaper than n pushes (O(n log n)), a favourite complexity question.
Where it's used
OS schedulers pick the next task by priority; Dijkstra pops the closest node; timers fire the soonest deadline; load balancers pop the least-loaded server. Python's heapq is a min-heap over a plain list.
In interviews
Kth largest element, top-k frequent, merge k sorted lists, median from a data stream (two heaps), meeting rooms II, task scheduler.
Analogy: An emergency room: patients (values) arrive in any order, but the desk always knows who's most critical (the root). Discharging them promotes the next-most-critical in a few comparisons — nobody re-sorts the whole waiting room.
Interactive diagram
The new value enters at the only shape-legal slot, then swaps upward until its parent is smaller.
Min-heap before insert
Array form: [3, 7, 5, 12, 9, 8]. Parent of index i lives at (i − 1) // 2 — no pointers needed.
1 / 5
Lessons in this topic
Check off lessons as you go — your progress is saved in this browser.
Heap property and array representation
Complete shape → index arithmetic instead of pointers.
20 min
Insert and sift-up
Append, then bubble toward the root while violating.
20 min
Extract and sift-down
Last element to root, swap with the SMALLER child.
20 min
Heapify in O(n)
Bottom-up sift-downs and why the sum telescopes to O(n).
20 min
Python's heapq (and max-heap tricks)
Negation, (priority, item) tuples, tie-breaking.
15 min
Top-k and k-th largest
A size-k min-heap of the best-so-far: O(n log k).
25 min
Merge k sorted lists
A k-sized heap of list heads: O(N log k).
20 min
Median from a data stream
Max-heap of the low half + min-heap of the high half.
20 min
Operations
Insert (sift-up)
Append at the array end — completeness preserved — then repair the ordering upward: at most one swap per level.
Min-heap before insert
Array form: [3, 7, 5, 12, 9, 8]. Parent of index i lives at (i − 1) // 2 — no pointers needed.
1 / 5
1defheap_push(heap:list[int],value:int)->None:2"""Min-heap insert. O(log n)."""3heap.append(value)# only legal slot (completeness)4i=len(heap)-15whilei>0:6parent=(i-1)//27ifheap[parent]<=heap[i]:# order restored8break9heap[parent],heap[i]=heap[i],heap[parent]10i=parent
Time: O(log n) worst, O(1) average for random valuesSpace: O(1)
Edge cases
Empty heap: the value becomes the root, loop never runs.
Value equal to parent: stop (≤, not <) — needless swaps waste time and can break tie stability expectations.
New global minimum bubbles all the way to index 0.
Common mistakes
Inserting at the root and sifting down (breaks completeness).
Wrong parent formula — it's (i − 1) // 2, not i // 2.
Extract-min (sift-down)
Root out, last element in, then swap downward with the SMALLER child until both children are larger.
Pop the minimum
The root (2) is the answer — a min-heap always keeps its smallest value at index 0.
1 / 4
1defheap_pop(heap:list[int])->int:2"""Remove and return the minimum. O(log n)."""3ifnotheap:4raiseIndexError("pop from empty heap")5minimum=heap[0]6last=heap.pop()7ifheap:8heap[0]=last# fill the hole, then repair down9i,n=0,len(heap)10whileTrue:11left,right=2*i+1,2*i+212smallest=i13ifleft<nandheap[left]<heap[smallest]:14smallest=left15ifright<nandheap[right]<heap[smallest]:16smallest=right17ifsmallest==i:18break19heap[i],heap[smallest]=heap[smallest],heap[i]20i=smallest21returnminimum
Time: O(log n)Space: O(1)
Edge cases
Single element: pop it, no sift needed.
A node with only a left child (last internal node) — the right-bound checks matter.
Popping empty must raise.
Common mistakes
Swapping with ANY smaller child instead of the SMALLEST — the promoted value can still exceed the other child.
Forgetting the heap-emptied case after pop().
Top-k pattern (size-k heap)
Keep a min-heap of the k best seen so far; anything beating the heap's minimum replaces it. n items, log k work each.
Top-k pattern (size-k heap)
1importheapq234defk_largest(nums:list[int],k:int)->list[int]:5"""k largest values. O(n log k) time, O(k) space."""6heap:list[int]=[]7forxinnums:8iflen(heap)<k:9heapq.heappush(heap,x)10elifx>heap[0]:# beats the weakest of the best11heapq.heapreplace(heap,x)# pop+push in one O(log k)12returnsorted(heap,reverse=True)131415defkth_largest(nums:list[int],k:int)->int:16returnk_largest(nums,k)[-1]
Time: O(n log k) — beats full-sort O(n log n) when k ≪ nSpace: O(k)
Edge cases
k ≥ n: everything qualifies.
Duplicates: x > heap[0] keeps equal values out; use >= if ties should replace.
Streaming input: the same loop works online — the heap IS the state.
Common mistakes
Using a max-heap of ALL n items (O(n) space) when O(k) was the point.
Min/max confusion: k LARGEST needs a MIN-heap of candidates.
Complexity analysis
Operation
Best
Average
Worst
Space
peek min/max
O(1)
O(1)
O(1)
—
push / pop
O(1)
O(log n)
O(log n)
O(1)
heapify n items
O(n)
O(n)
O(n)
O(1)
search arbitrary value
O(1)
O(n)
O(n)
—
heap sort
O(n log n)
O(n log n)
O(n log n)
O(1)
The search row is the trade-off: heaps order only along root-to-leaf paths. Need arbitrary lookup too? Pair the heap with a hash map (or use a BST).
Python implementation
Production-quality code with type hints, validation, and docstrings.
Median from a data stream (the two-heap design)
1importheapq234classMedianFinder:5"""Runningmedian:max-heapofthelowhalf,min-heapofthehighhalf.6add:O(logn)·median:O(1)."""78def__init__(self)->None:9self._low:list[int]=[]# max-heap via negation10self._high:list[int]=[]# min-heap1112defadd(self,num:int)->None:13# 1. route: everything ≤ low's max goes low14ifself._lowandnum>-self._low[0]:15heapq.heappush(self._high,num)16else:17heapq.heappush(self._low,-num)18
What interviewers expect you to know
What interviewers expect you to know
Array indexing: children 2i+1 / 2i+2, parent (i−1)//2 — no pointer version exists in practice.
Heapify is O(n), not O(n log n): most nodes are near the leaves and sift almost nowhere; the level sums telescope.
heapq is min-only: negate for max-heaps, or push (priority, tiebreak, item) tuples.
A heap is NOT a sorted array: only root-to-leaf paths are ordered; the array read left-to-right is not sorted.
Pattern triggers
'k largest / smallest / most frequent / closest' → size-k heap, O(n log k).
'merge k sorted …' → heap of k heads, O(N log k).
'running median / percentile' → two heaps.
'schedule by earliest deadline / smallest end time' → heap inside a greedy (meeting rooms II).
Follow-ups to expect
"Why is heapify O(n)?" — sift-down cost is the node's height; Σ n/2^(h+1) · h = O(n).
"Heap vs BST?" — heap: cheaper, cache-friendly, min-only; BST: full ordering, arbitrary search, successor queries.
"Decrease-key?" — heapq lacks it; standard workaround is lazy deletion (push the new entry, skip stale ones on pop) — the Dijkstra idiom.
Common mistakes
Treating the heap array as sorted
Only parent-child pairs are ordered. heap[1] is NOT the second smallest necessarily — pop twice if you need the second smallest.
Sifting down with the wrong child
Swap with the SMALLER child (min-heap). Swapping with the larger leaves a violation behind.
O(n log n) heapify
Pushing n items one by one is O(n log n); bottom-up heapify is O(n). Saying the first when asked for the second costs points.
Max-heap improvisation errors
Negate on push AND on read. Mixed sign conventions produce silently wrong answers.
Unstable tuple comparisons
Pushing (priority, object) crashes when priorities tie and objects aren't comparable. Add a counter: (priority, seq, object).
Practice problems
Hand-picked from the 355-problem DSA Atlas. Reveal hints and solutions progressively; mark and bookmark as you solve.
HardSort by ratio, sliding max-heap of qualities~40 min
Commonly associated with: Google, Amazon, Uber
O(n log n) time · O(n) space
Topic quiz
5 questions across concepts, code output, complexity, and scenarios. Answer all, then submit for explanations.
Frequently asked questions
How do I get a max-heap in Python?
Negate: push -x, read -heap[0]. For objects, push (-priority, seq, item) with an incrementing seq to break ties. (heapq._heapify_max exists but is private — don't rely on it.)
Heap sort vs quicksort/mergesort — when would I pick it?
Heap sort gives guaranteed O(n log n) AND O(1) extra space — the only mainstream sort with both. It loses on constants and cache behaviour, so in practice it's a fallback (introsort switches to it when quicksort recursion gets deep).
When do I need a d-ary or Fibonacci heap?
Interview answer: almost never — name-drop only. d-ary heaps trade cheaper sift-down for costlier sift-up (good when pushes dominate); Fibonacci heaps improve Dijkstra's theoretical bound with amortised O(1) decrease-key but lose in practice.
Summary & cheat sheet
Key takeaways
Complete shape + parent ≤ children: min at root, tree in an array.
push/pop O(log n); peek O(1); heapify O(n).
Top-k = size-k min-heap; k-way merge = heap of heads; running median = two heaps.
heapq is min-only — negate for max.
Heaps sacrifice arbitrary search; pair with a map when you need both.
Formulas & cheat sheet
children(i) = 2i+1, 2i+2 · parent(i) = (i−1)//2
heapify: Σ (n / 2^(h+1)) · h = O(n)
top-k: O(n log k) · k-way merge: O(N log k)
Interview checklist
I can implement sift-up and sift-down from memory.
I can explain O(n) heapify with the telescoping sum.
I can code kth-largest with a size-k heap and state why min-heap.