Interactive Python notes for heap intuition, representation, heapq, operations, complexity, and practice patterns.
Think of a heap like an ER triage desk. Patients arrive in any order, but the most critical patient always gets seen first — regardless of who arrived when.
A heap doesn't sort everything — it only guarantees the min (or max) is always accessible instantly. That's why it's O(1) peek and O(log n) insert/delete. You sacrifice full ordering for lightning-fast extremes.
Use a heap when you repeatedly need the smallest or largest element from a dynamic dataset.
All levels are fully filled except possibly the last, which is filled left to right.
Min-heap: every node ≤ its children
| Property | Min-Heap | Max-Heap |
|---|---|---|
| Node vs children | parent ≤ children | parent ≥ children |
| Root value | Minimum element | Maximum element |
| Subtrees | Also min-heaps | Also max-heaps |
A heap is NOT fully sorted. Siblings have no ordering relationship with each other.
Root = 1 (minimum)
Python's default via heapq
Root = 9 (maximum)
Use negation trick in Python
| Use Min-Heap for | Use Max-Heap for |
|---|---|
| Dijkstra's shortest path | Kth largest element |
| Task scheduling (low priority #) | Top K frequent elements |
| Merge K sorted lists | Sliding window maximum |
| Median maintenance | Priority queue (max first) |
| Relation | Formula | Example (node at i=1) |
|---|---|---|
| Left child | 2*i + 1 | 2*1+1 = 3 → value 7 |
| Right child | 2*i + 2 | 2*1+2 = 4 → value 5 |
| Parent | (i-1) // 2 | (3-1)//2 = 1 → value 3 |
No pointers needed! The array IS the tree. This is why heaps use O(n) space with no overhead.
| Function | What it does | Time |
|---|---|---|
| heapq.heappush(h, x) | Insert x into heap h | O(log n) |
| heapq.heappop(h) | Remove & return minimum | O(log n) |
| heapq.heapify(list) | Turn list into heap in-place | O(n) |
| heapq.nsmallest(k, it) | k smallest elements | O(n log k) |
| heapq.nlargest(k, it) | k largest elements | O(n log k) |
| heapq.heappushpop(h, x) | Push then pop (efficient) | O(log n) |
| h[0] | Peek at minimum | O(1) |
Python's heapq only supports min-heap. For max-heap, negate values (see Max Heap tab).
Inserting 2 into heap [1, 4, 3, 7, 9]
Why O(n) and not O(n log n)? Most nodes are near the bottom (short travel distance). Mathematically, the sum of all bubble-down distances converges to O(n).
Python's heapq is always a min-heap. To simulate max-heap, negate all values on push and un-negate on pop.
Min-heap of [-9, -7, -5] = Max-heap of [9, 7, 5]
For tuples: only negate the comparison key (first element). The rest stays unchanged.
| Operation | Time | Why |
|---|---|---|
| Peek (min/max) | O(1) | Root is always at index 0 |
| Insert (push) | O(log n) | Bubble up ≤ tree height |
| Delete min/max | O(log n) | Sift down from root |
| Build heap (heapify) | O(n) | Most nodes near leaves |
| Delete arbitrary | O(n) | Search O(n) + fix O(log n) |
| Search | O(n) | No ordering between siblings |
| Merge two heaps | O(n) | Concat + heapify |
| nsmallest(k, n) | O(n log k) | Maintain size-k heap |
| Structure | Insert | Get Min | Delete Min |
|---|---|---|---|
| Sorted array | O(n) | O(1) | O(n) |
| Unsorted array | O(1) | O(n) | O(n) |
| BST (balanced) | O(log n) | O(log n) | O(log n) |
| Heap ✓ | O(log n) | O(1) | O(log n) |
Find the kth largest element in an unsorted array. Strategy: maintain a min-heap of size k. If heap size exceeds k, pop the minimum. The root is your answer.
Count frequencies, then use a min-heap of size k sorted by frequency.
Use a min-heap to always pick the smallest node across all k lists.
Include i (list index) as tiebreaker to avoid comparing ListNode objects.
Reach for a heap when you see:
| Pattern | Heap type | Size |
|---|---|---|
| Kth largest | Min-heap | k |
| Kth smallest | Max-heap | k |
| All K largest | Min-heap | k (root = kth) |
| Merge K lists | Min-heap | k heads |
| Median stream | 2 heaps (max+min) | balanced |
Two-heap pattern for median: max-heap for lower half, min-heap for upper half. Keep sizes equal (or differ by 1). Median is the root(s).
Avoid using nsmallest/nlargest in interviews if k is large — prefer building and maintaining your own heap for clarity.