8.1 Heaps & Priority Queues (Top-K & Two Heaps)
Understand what a Binary Heap is, how it stores a tree inside a flat array, and how to solve Top-K and Streaming Median problems in O(N log K).
1. What Is a Heap (Priority Queue)?
A Binary Heap (also called a Priority Queue) is a complete binary tree where every parent node obeys a simple priority rule over its children:
- Min-Heap: Every parent is
<=its children. That guarantees the smallest item in the entire collection is always sitting at the root (index 0)! - Max-Heap: Every parent is
>=its children. That guarantees the largest item is always sitting at the root (index 0)!
Because a Heap is always filled top-to-bottom, left-to-right with no gaps, it doesn't even need TreeNode pointers — it lives inside a plain flat array where the children of index i sit at 2*i + 1 and 2*i + 2!
2. Why Do We Need a Heap & How Does It Work?
The Pain Without a Heap
Suppose items are arriving in a stream (emergency room patients, task priorities, live stock bids) and you repeatedly need to add a new item and pull out the highest/lowest priority item:
- Unsorted Array: Adding is
O(1), but finding the min/max takesO(N). - Sorted Array: Finding the min/max is
O(1), but inserting a new item shifts the array inO(N). - Binary Heap: Peeking at the min/max is
O(1), and pushing or popping only bubbles up/down the height of the tree inO(log N)!
How Sift-Up (push) and Sift-Down (pop) Work Inside the Array
heappush(x)— Bubble Up inO(log N): Placexat the very end of the array (bottom-right leaf). Comparexwith its parent at(i - 1) // 2. Whilexis smaller than its parent, swap them! Since a binary tree ofNnodes has heightlog2(N),xswaps at mostlog2(N)times.heappop()— Bubble Down inO(log N): Save the rootheap[0]to return. Move the last element of the array intoheap[0], and repeatedly swap it down with its smaller child (2*i + 1or2*i + 2) until the Min-Heap rule holds again.heapify(arr)— Build a Heap from an Unsorted Array inO(N)Time: Most candidates assume building a heap ofNitems takesO(N log N). In fact, callingheapq.heapify(arr)starts from the bottom-most internal nodes and sifts down, running in linearO(N)time because half the nodes are leaves that do0work!
Warm-Up Problem Before Top-K: Last Stone Weight
Suppose you have stones [2, 7, 4, 1, 8, 1]. Every turn, you smash the two heaviest stones y and x together:
- Put all stones into a Max-Heap.
- Pop
8and7-> push8 - 7 = 1back into the heap (O(log N)). - Repeat until at most 1 stone remains!
3. Basic Operations & Syntax (All 4 Languages)
import heapq
# 1. Min-Heap (default in Python heapq)
min_heap = []
heapq.heappush(min_heap, 5)
heapq.heappush(min_heap, 1)
heapq.heappush(min_heap, 3)
smallest = min_heap[0] # peek -> 1 (O(1))
popped = heapq.heappop(min_heap) # pop -> 1 (O(log N))
# 2. Max-Heap in Python: Multiply values by -1 when pushing & popping!
max_heap = []
heapq.heappush(max_heap, -10)
heapq.heappush(max_heap, -40)
largest = -max_heap[0] # peek -> 404. Core Interview Patterns
Pattern A: Top-K Largest Elements (Keep a Min-Heap of Size K)
Why use a Min-Heap to find the K largest elements?
Think of the heap like a VIP club with only K seats. Every time a new person arrives and the club has K + 1 people, who gets kicked out? The weakest (smallest) person in the club! A Min-Heap keeps the smallest member right at the door (heap[0]), so kicking them out takes O(log K). At the end, the K strongest (largest) members remain, and heap[0] is the K-th largest!
Interactive Step-by-Step Trace: Size-K Min-Heap for Top-K Largest
Find the Top K = 3 largest numbers in [3, 2, 1, 5, 6, 4]:
The Min-Heap keeps the weakest member of the Top-3 VIP club right at index 0.
Pattern B: Two Heaps for Streaming Median (Find Median from Data Stream)
Maintain a Max-Heap (small) for the lower half of numbers and a Min-Heap (large) for the upper half of numbers. Keep their sizes within 1 of each other so the median is always sitting right at the top of the heaps in O(1)!
Use a Min-Heap capped at size K for Top-K Largest/Closest/Frequent problems; use a Max-Heap + Min-Heap pair to maintain a live median.
# 1. Kth Largest Element via Size-K Min-Heap:
heap = []
for x in nums:
heapq.heappush(heap, x)
if len(heap) > k:
heapq.heappop(heap) # Evict smallest of the K+1 items
return heap[0] # Kth largest sits at root!| Operation / Pattern | Time | Space | Notes |
|---|---|---|---|
| Peek Min / Max | O(1) | O(1) | Root at index 0 |
| Push / Pop Element | O(log N) | O(1) | Sifts up or down tree height |
| Top-K Elements | O(N log K) | O(K) | Faster than O(N log N) full sort when K << N |