Heap & Top-K: Beginner
What you will be able to do
heapq Module Fundamentals
Use heapq confidently for any heap operation
- ▸Python's heapq is a MIN-heap only. heappop() returns the SMALLEST element
- ▸To simulate a max-heap, negate all values before pushing and negate again on pop
- ▸heapq.heapify(list) transforms a list in-place in O(n), beating a full sort
- ▸heapq.nlargest(k, iterable) and nsmallest(k, iterable) are convenience wrappers
- ▸heappush and heappop are both O(log n). That log n cost is the key to all heap efficiency
nlargest vs sorted: When to Use Each
- O(n log n) always
- Sorts the entire collection
- Simpler code: one line
- Better only when k is close to n
- O(n log k) time
- Processes stream without storing all
- Ideal when k << n
- Better for top-10 of 10 million rows
- ▸"I could sort and slice, which is O(n log n), but since k is much smaller than n, I'll maintain a heap of size k for O(n log k). On a dataset of 10 million rows with k=10, that's roughly 10 million * 4 operations vs 10 million * 23 operations, so about 6x faster."
Top-K Largest and Smallest: The Canonical Pattern
Implement top-K with O(n log k) heap pattern
The Size-K Min-Heap Pattern for Top-K Largest
heapq.heapreplace(heap, item) is an atomic pop-then-push that is faster than calling heappop() and heappush() separately. Use it whenever you know the heap is non-empty and you want to swap the minimum for a new value. In a tight loop over millions of records, this matters.
Why log k Matters When k << n
Kth Largest Element: Two Approaches
Solve Kth largest with both approaches and explain tradeoffs
Approach 1: Sort
Approach 2: Min-Heap of Size K
- ▸"Yes. I can maintain a size-k min-heap as I scan the array. Every element is either pushed or discarded in O(log k). Total time is O(n log k), which is asymptotically better when k is much smaller than n."
- ▸Bonus: mention QuickSelect (O(n) average) if they push further, but do not lead with it unless asked
| Approach | Time | Space | When to Use |
|---|---|---|---|
| sorted()[:k] | O(n log n) | O(n) | k close to n, or n is small, or code clarity matters |
| Min-heap of size k | O(n log k) | O(k) | k << n, streaming data, memory-constrained |
| QuickSelect | O(n) avg, O(n²) worst | O(1) | n is huge, k is exact, can tolerate worst case |
| heapq.nlargest(k) | O(n log k) | O(k) | Same as min-heap; use when code brevity is fine |
The DE use case that comes up constantly: find the top-10 highest revenue customers from a table with 50 million rows. You cannot sort 50 million rows just to get 10. You maintain a size-10 heap. The heap approach is not academic. It is what you do when the data does not fit in sorted memory.
Heap with Tuples: Priority and Tie-Breaking
Use tuple heaps safely with correct tie-breaking
Priority Queue with (priority, item) Tuples
Lexicographic Comparison: Useful and Occasionally a Gotcha
- ▸If two tuples tie on the first element, Python compares the second element
- ▸If the second element is a custom object without __lt__, you get: TypeError: '<' not supported
- ▸Fix: always insert a unique counter as the second element: (priority, counter, item)
- ▸This guarantees no tuple comparison ever reaches the custom object
- Safe Pattern
- Readable Pattern
- Inversion Pattern
Basic DE Applications
Apply heap patterns to real DE problems confidently
Top-K Most Frequent Log Errors
Finding the K Slowest Queries
K Closest Timestamps to a Target
- ▸Top-K frequent: Counter + nlargest(k, counts, key=counts.get), for log errors, query patterns, user events
- ▸Top-K by metric: nlargest(k, records, key=lambda r: r.metric), for slowest queries, highest revenue, most errors
- ▸K closest: nsmallest(k, records, key=lambda r: abs(r.value - target)), for timestamp alignment, watermark skew analysis
- Always state the O(n log k) complexity and contrast with O(n log n) sort
- Use heapq.nlargest/nsmallest for clean one-liners in interviews when brevity helps
- Explicitly mention the DE analogy after solving: 'same as ORDER BY revenue DESC LIMIT 10'
- Handle the edge case: if k >= len(data), return all elements sorted
- Never sort the entire collection just to take the top K when n is large
- Don't forget to negate values when simulating a max-heap with numeric data
- Don't use heappush on an unhashable or uncomparable object without a counter guard
> You are in a phone screen. The interviewer asks: 'Given a list of log entries with error types and counts, find the 5 most frequent error types as efficiently as possible.'
Stop sorting the whole list. Keep only what you need.
- Category
- Python
- Difficulty
- beginner
- Duration
- 25 minutes
- Challenges
- 0 hands-on challenges
Topics covered: heapq Module Fundamentals, Top-K Largest and Smallest: The Canonical Pattern, Kth Largest Element: Two Approaches, Heap with Tuples: Priority and Tie-Breaking, Basic DE Applications
Lesson Sections
- heapq Module Fundamentals (concepts: pyHeapqModule, pyMinHeap, pyMaxHeapSimulation)
Every heap interview problem in Python starts with one fact: heapq only gives you a min-heap. The root is always the smallest element. When you heappop(), you get the smallest. When you heappush(), the heap rebalances to maintain that invariant in O(log n). This is not a bug; it is by design. The reason Python only provides a min-heap is that a max-heap is trivially simulated by negating values, and providing both would add library surface area for no real gain. The interviewer knows this, and t
- Top-K Largest and Smallest: The Canonical Pattern (concepts: pyTopKLargest, pyTopKSmallest, pyHeapReplace)
This is the most important heap pattern you will ever learn for interviews. Finding the top-K largest elements from a list of N elements. The naive approach: sort descending, take first K. O(n log n). The heap approach: maintain a min-heap of size exactly K. For every element you process, if it is larger than the heap's minimum (the root), push it in and pop the minimum out. At the end, the heap contains exactly the K largest elements. Time: O(n log k). Space: O(k). The Size-K Min-Heap Pattern f
- Kth Largest Element: Two Approaches (concepts: pyKthLargest, pyMinHeapTopK, pyQuickSelect)
LeetCode 215 is one of the most frequently asked heap problems at FAANG and FAANG-adjacent companies. Find the Kth largest element in an unsorted array. It is deceptively simple, but interviewers use it to filter candidates who know the theory from candidates who know when to apply which tool. There are two approaches you need to know: sort (simple, O(n log n)) and heap (efficient, O(n log k)). You should be able to code both and explain when each is appropriate. Approach 1: Sort The sort approa
- Heap with Tuples: Priority and Tie-Breaking (concepts: pyHeapTuples, pyTieBreaking, pyIterToolsCount)
Python heaps compare tuples lexicographically: first by the first element, then by the second if there is a tie, then by the third. This is incredibly useful for priority queues where you want to order by one field and break ties by another. It is also a gotcha: if two tuples have the same priority value and the second element is an uncomparable type (like a custom object without __lt__), heapq will raise a TypeError. Knowing this cold is how you avoid a humiliating bug in a live interview. Prio
- Basic DE Applications (concepts: pyTopKFrequent, pySlowestQueries, pyKClosest)
Here is where you convert a coding answer into a data engineering answer. Heap and top-K problems are not just LeetCode exercises. They show up constantly in DE work: finding the most frequent log errors to prioritize, identifying the slowest queries for optimization, finding the K closest events to a target timestamp for alignment. Every time you frame your heap solution in terms of real DE problems, the interviewer writes 'strong domain understanding' on the scorecard. Top-K Most Frequent Log