Top-K problems
In one line
To find the k largest items, keep a min-heap of size k: its top is the weakest of the current winners, so each new item either beats it and replaces it or is ignored, for O(n log k) time and O(k) memory.
What it is
Top-K problems ask for the k largest, smallest, most frequent or closest items, or for just the k-th one. Sorting everything works in O(n log n), but it does more than needed and needs all the data in memory.
The heap trick is counter-intuitive: for the k largest you use a min-heap, and for the k smallest a max-heap. The heap holds the current best k, and its top is the worst of them, the one that would be kicked out next. Each new item is compared with that top in O(1), and only winners pay O(log k) to enter. The heap never grows beyond k, so it also works on a stream of unknown length.
Why interviewers ask
Top-K is one of the most frequent heap question types: Kth Largest Element (LeetCode 215), Top K Frequent Elements (347), K Closest Points to Origin (973) and Kth Largest in a Stream (703). Interviewers want the size-k heap with the right direction, the O(n log k) analysis, and awareness of alternatives like quickselect (O(n) average) and bucket sort for frequencies.
How it works
- 1Decide the direction. k largest: min-heap (the top is the smallest of the winners). k smallest or k closest: max-heap (in Python, push negated keys).
- 2For each item: if the heap has fewer than k items, push it.
- 3Otherwise compare the item with the top. If it is better than the top, replace the top with it (heapreplace pops and pushes in one O(log k) step). If not, skip it.
- 4At the end the heap holds exactly the top k items in no particular order; sort them if order is needed (O(k log k)). The k-th largest is simply the top of the min-heap.
- 5For 'most frequent', first count with a hash map, then run the same size-k heap over (count, value) pairs.
Worked example · 3 largest of [3, 1, 5, 12, 2, 11]
- 3: heap has fewer than 3 items, push -> {3}.
- 1: push -> {1, 3}. 5: push -> {1, 3, 5}, top = 1. The heap is now full.
- 12: 12 > top 1, replace -> {3, 5, 12}, top = 3.
- 2: 2 > top 3? No, skip. The heap is untouched.
- 11: 11 > top 3, replace -> {5, 11, 12}, top = 5.
- Result: sorted descending [12, 11, 5]. The 3rd largest is the top, 5. Only 5 heap operations happened, each on a heap of at most 3 items.
Picture it
It is like a talent show that only has k chairs on stage. The judge watches only the weakest person seated. A new act either beats that person and takes the chair, or goes home. The judge never needs to rank everyone who auditioned.
Reference code
import heapq
from collections import Counter
def k_largest(nums, k):
heap = [] # min-heap holding the k biggest so far
for x in nums:
if len(heap) < k:
heapq.heappush(heap, x)
elif x > heap[0]: # beats the weakest of the current top k
heapq.heapreplace(heap, x) # pop the smallest, push x
return sorted(heap, reverse=True) # heap[0] alone is the k-th largest
def top_k_frequent(nums, k):
heap = []
for val, count in Counter(nums).items():
heapq.heappush(heap, (count, val))
if len(heap) > k:
heapq.heappop(heap) # drop the least frequent
return [val for count, val in sorted(heap, reverse=True)]k_largest keeps a min-heap of the best k and replaces the top only when a new item beats it. top_k_frequent counts first, then keeps the k most frequent (count, value) pairs by popping the smallest whenever the heap exceeds k. Both are O(n log k) time and O(k) heap memory (plus O(u) for the counter over u distinct values).
Key patterns
K-th largest element
When: LeetCode 215, or a stream version (LeetCode 703)
How: Min-heap of size k; the answer is heap[0]. For a stream, keep the heap between calls.
Top k frequent
When: Most common words, IPs or elements (LeetCode 347, 692)
How: Count with a hash map, then size-k min-heap on (count, value). Bucket sort by count is O(n) alternative.
K closest points
When: Nearest k points to a target (LeetCode 973)
How: Max-heap of size k by squared distance (push the negative); pop when size exceeds k.
Quickselect alternative
When: All data is in memory and you only need the k-th item
How: Partition like quicksort and recurse into one side: O(n) average, O(n^2) worst case, modifies the array.
Real-world use
Search result ranking
Lucene, the engine inside Elasticsearch and Solr, collects the top k scoring documents for a query with a fixed-size priority queue, so it never sorts the millions of documents that matched.
Database ORDER BY ... LIMIT
PostgreSQL's top-N heapsort keeps only the N best rows while scanning, which is why EXPLAIN ANALYZE shows 'Sort Method: top-N heapsort' with small memory use for queries with a small LIMIT.
Trending lists and leaderboards
Systems that show 'top 10 trending hashtags' or 'top 100 products' count events and maintain a size-k heap over the counts; at very large scale they pair it with approximate counters like count-min sketch.
Nearest-neighbour search
Vector search libraries such as FAISS keep the k closest candidates found so far in a heap while scanning or searching the index, then return them sorted by distance.
In a project
You run an API gateway that logs 50 million requests a day and need a dashboard of the 20 slowest endpoints and the 20 IP addresses with the most requests, updated every minute.
- Stream each minute's log lines once, keeping a hash map of request counts per IP and the worst latency per endpoint.
- Feed the counts into a min-heap of size 20 keyed by count, and the latencies into another size-20 min-heap keyed by latency.
- Sort the 20 survivors for display and push them to the dashboard.
- If memory for per-IP counts grows too large, switch the counts to a count-min sketch and keep the size-20 heap of heavy hitters.
Impact: Each minute is processed in a single pass with O(n log 20) work and tiny extra memory, instead of sorting millions of rows, so the dashboard updates in seconds and the on-call engineer sees abusive IPs and slow endpoints almost live.
Trade-offs
Use it when
- k is much smaller than n, so O(n log k) clearly beats O(n log n)
- Data arrives as a stream or does not fit in memory
- You need the k-th item repeatedly as new data arrives
Avoid it when
- k is close to n: just sort
- All data is in memory and you only need one k-th value: quickselect is O(n) on average
- Frequencies are bounded by n: bucket sort by count gives O(n)
Cost: Size-k heap: O(n log k) time, O(k) space. Sorting: O(n log n). Quickselect: O(n) average, O(n^2) worst. heapq.nlargest(k, data) uses the same size-k heap idea.
Common mistakes
- Using a max-heap of all n items for the k largest, which costs O(n) space and O(n + k log n) time and does not work for streams.
- Picking the wrong direction: for the k largest the heap must be a min-heap so the weakest winner is on top.
- Assuming the final heap is sorted; it is not, so sort it if the order matters.
- Computing distances with square roots for 'k closest' when comparing squared distances is enough and avoids float error.
- Forgetting tie-breaking rules, such as LeetCode 692 asking for alphabetical order among equal counts.
Interview questions
Quick revision
- k largest -> min-heap of size k; k smallest -> max-heap of size k.
- New item beats the top? heapreplace. Otherwise skip.
- K-th largest = top of the size-k min-heap.
- Time O(n log k), space O(k); works on streams.
- Frequencies: hash map count first, then heap (or bucket sort O(n)).
- Quickselect: O(n) average, O(n^2) worst, in memory only.
- Result heap is not sorted: sort the k items at the end if needed.
Check yourself
1. To keep the 10 largest values from a stream, which heap do you use?
2. What is the time complexity of finding the k largest of n items with a size-k heap?
3. After processing [3, 1, 5, 12, 2, 11] with a size-3 min-heap, what is at the top?
4. When is quickselect a better choice than a heap for the k-th largest?
Saved on this device. Create a free account to keep it on every device.
Practise this with Sam
A real mock interview, out loud, with an honest scorecard. Free to start.