Reach for a heap when you need to rank entries on the go and keep fetching the top one (the smallest or the largest). Sorting ranks everything once; a heap keeps the ranking correct while items arrive and leave. The most common use is Top K: keep only the K best items seen so far and throw the rest away.
The idea#
Python's heapq works on a plain list and is a min-heap only: the smallest item always sits at heap[0]. These are the calls you will use most:
| Operation | Cost | Notes |
|---|---|---|
heap[0] |
peek at the smallest | |
heappush(h, x), heappop(h) |
sift up / sift down | |
heapify(lst) |
in place, not | |
heapreplace(h, x) |
pop the smallest, then push x |
|
heappushpop(h, x) |
push x, then pop the smallest |
Before Python 3.14 there is no public max-heap API, and LeetCode runs older versions. To track the largest values, negate them on the way in and negate them back on the way out.
Top K: keep the weakest link on top#
The heap you need is the opposite of what the question asks for, which feels backward at first:
- K largest → a min-heap of size K. The smallest item in the heap is the "weakest link". When a bigger item arrives, evict the weakest link.
- K smallest → a max-heap (negated values) of size K. Evict the biggest.
Each of the N items costs at most one heap operation, so the total is time and space. When K is small this beats sorting (), and it works on a stream you can't hold in memory.
How to spot it#
These cues point to a heap, and to the size-k version in particular.
- "Kth largest / smallest", "top K", "K closest", "K most frequent".
- Repeatedly take the largest (or smallest) item, change it, and put it back (a simulation).
- Data arrives as a stream and you must answer after every insert.
- N is large but K is small, or you can't store everything.
Template#
Here is the weakest-link rule as code: a size-k min-heap that keeps the K largest items by any key. Flip the sign of the key to get the K smallest.
heap = empty min-heap of (key, index, item) # heap[0] is the weakest
for each item, with its index:
entry = (key of item, index, item) # index breaks ties
if len(heap) < k:
push entry onto heap
else if entry > heap[0]:
replace heap[0] with entry # evict the weakest: O(log k)
return the items in heap # any order
After the loop, heap[0] is the Kth largest. When the key is a distance, compare squared distances: if then , so gives the same order with no square root, float cost or precision issue.
Other shapes#
Two more shapes solve some Top K problems faster or with ordered output.
Heapify, then pop K#
When all the data is already in memory, heapify the whole list () and pop K times (). The total, , is much better than pushing items one at a time (). It is also the easy way to get ordered output, because pops come out in order and tuple comparison breaks ties.
Bucket sort for frequencies#
For "top K by frequency", counts can't exceed N, so you can skip the heap. Put each value in the bucket for its count, then walk the buckets from the highest count down.
count each value
buckets = list of N + 1 empty lists # index = frequency
for each (value, freq) in counts:
append value to buckets[freq]
result = empty list
for freq from N down to 1:
for each value in buckets[freq]:
append value to result
if len(result) == k: return result
The nested loop looks like , but it isn't: each unique value sits in exactly one bucket and is visited once, so the walk is . It does not give alphabetical order within a bucket.
Counting sort for Kth largest#
If values sit in a small range, count them in an array indexed by num - min_value and walk down from the maximum until K items are used up. That's with no heap at all.
Which heap pattern?#
Most heap problems fall into one of these shapes.
| Goal | Tool | Why |
|---|---|---|
| K largest | min-heap of size K | pop the small ones, keep the giants |
| K smallest | max-heap of size K | pop the big ones, keep the tiny ones |
| Median of a stream | two heaps | one for the small half, one for the big half |
| Merge K sorted sources | min-heap of heads | next smallest among K candidates |
| Schedule tasks | heap + waiting queue | always run the most urgent or frequent task first |
A size-k heap (Top K) and a k-way merge are different patterns: one caps the heap at K items, the other keeps one item per source.
Pitfalls#
These are the slips that come up most often with heaps.
- Heapifying everything and calling it "size K". It's correct, but costs memory. When the heap starts from initial data, trim it to K there too, not only on later inserts.
- Forgetting to negate back when you read from a negated max-heap.
- Tuples with non-comparable items.
(priority, obj)raisesTypeErroron a tie whenobjhas no ordering (aListNode, a dict). Add an index as a tie-breaker, or wrap the object in a class with__lt__. - Picking the wrong heap for Top K. K largest needs a min-heap, not a max-heap.
- Re-sorting in a loop. Sorting again every round of a simulation costs ; a heap makes it .
Problems in this pattern#
These problems use this pattern. Solve them before you open the hints below. Start with the first three.
- 1046. Last Stone Weight
- 703. Kth Largest Element in a Stream
- 215. Kth Largest Element in an Array
- 347. Top K Frequent Elements
- 973. K Closest Points to Origin
- 451. Sort Characters By Frequency
- 692. Top K Frequent Words
Hints#
Open a hint only after you have tried the problem for a while.
Hint: Last Stone Weight
Each round needs the two largest stones, so use a max-heap of negated weights. Sorting every round is ; the heap makes it .
Hint: Kth Largest Element in an Array
Keep a min-heap of size K. After the scan, the heap top is the answer. If values sit in a small range, counting sort skips the heap.
Hint: Kth Largest Element in a Stream
Keep a min-heap of size K across calls; each add returns the top. Trim the heap to K in the constructor too, not only in add, so it never grows past K.
Hint: Top K Frequent Elements
Count first. Then either keep a size-K heap keyed by count, or bucket the values by count and walk down from the highest count for .
Hint: K Closest Points to Origin
Use the key , so "largest key" means "nearest". Skip the square root.
Hint: Sort Characters By Frequency
Bucket sort by count works in . Walk the buckets from the highest count down and repeat each character by its count.
Hint: Top K Frequent Words
Ties must come out in alphabetical order, so bucket sort is not enough. Heapify (-freq, word) pairs and pop K times: the highest count comes first, and tuple comparison breaks ties alphabetically.
Key takeaways#
heapqis a min-heap; negate values for a max-heap, and negate back when you read.- K largest uses a min-heap of size K (the top is the weakest link), giving .
heapifyis , so heapify-then-pop-K is .- Frequency problems can use bucket sort in unless ties need ordering.
- Put an index in the tuple (or define
__lt__) whenever the payload can't be compared.