Pattern 09 of 20
Heap / Priority Queue
Always know the smallest or largest item: top-k, merging streams, scheduling.
When to use it
- You need the k largest, smallest or most frequent items
- Merging several sorted sequences
- Repeatedly taking the best next option (the cheapest, the earliest)
The idea
A heap gives the minimum (or maximum) in O(1) and accepts or removes items in O(log n). Keeping a heap of size k instead of sorting everything is the classic win.
Typical cost O(n log k) time, O(k) space.
How it works
A heap keeps one extreme element (the smallest or the largest) available in O(1), while inserting and removing cost O(log n). It never sorts everything; it only maintains enough order to know what comes next. That is cheaper than sorting when you only need the next few items.
For top-k, the counterintuitive move is to keep a min-heap of size k. Each new item competes with the weakest of the current top k, which sits at the root. The heap never grows past k, so the cost is O(n log k) instead of O(n log n).
Heaps also merge many sorted sources. Seed the heap with the first element of each list; every time you pop the smallest, push the next element from the same list. The heap holds at most one candidate per source.
In disguise, a heap appears in scheduling (always run the task that finishes first), in simulations (repeatedly take the two largest stones), in running medians (two heaps balanced around the middle), and inside Dijkstra's algorithm.
How to attack it
- 1Ask whether you repeatedly need the current minimum or maximum of a changing set.
- 2Pick min-heap or max-heap, and the size bound if it is a top-k question.
- 3Decide what to store: a value, or a tuple with a priority first and a tie-breaker.
- 4Write the push and pop loop, and what triggers each.
- 5Compare with sorting once: if the set never changes, a sort may be simpler.
Template
A skeleton to adapt, not a solution to paste. Write it from memory a few times.
import heapq
def k_largest(nums, k):
# Min-heap of size k: the root is the weakest of the current top k.
heap = []
for x in nums:
heapq.heappush(heap, x)
if len(heap) > k:
heapq.heappop(heap) # drop the smallest
return sorted(heap, reverse=True)
def merge_sorted(lists):
heap = []
for i, lst in enumerate(lists):
if lst:
# (value, list index, position): the index breaks ties safely.
heapq.heappush(heap, (lst[0], i, 0))
out = []
while heap:
val, i, j = heapq.heappop(heap)
out.append(val)
if j + 1 < len(lists[i]):
heapq.heappush(heap, (lists[i][j + 1], i, j + 1))
return out
# Python only has a min-heap: push -x to simulate a max-heap.Variants
- Top k with a bounded heap
- A min-heap of size k for the k largest (or a max-heap for the k smallest); O(n log k).
- K-way merge
- One candidate per sorted source in the heap; pop the smallest and push its successor.
- Two heaps for a median
- A max-heap for the lower half and a min-heap for the upper half, rebalanced so their sizes differ by at most one.
- Greedy scheduling
- Sort events by start time and keep a heap of end times or of available work, always taking the best next option.
- Lazy deletion
- Instead of removing arbitrary items, mark them stale and skip them when they reach the top.
Pitfalls
- Using a max-heap of all n items for top-k, which costs O(n log n) and O(n) memory.
- Pushing tuples whose second element is not comparable (Python compares tuples element by element on ties).
- Forgetting that Python's heapq is a min-heap and not negating values for max-heap behavior.
- Assuming the heap's underlying array is sorted; only the root is guaranteed.
- Sorting the JavaScript array on every insert instead of writing or using a real heap.
What it costs
| Operation | Cost |
|---|---|
| Peek min / max | O(1) |
| Push / pop | O(log n) |
| Heapify an array | O(n) |
| Top k of n items | O(n log k) time, O(k) space |
| Merge k sorted lists, N items total | O(N log k) |
30 Heap / Priority Queue problems
Easiest first. Problems marked Free can be solved without Pro.
- 1K-th Loudest ReadingFreeEasy
- 2Boulder CrusherFreeEasy
- 3Closest Readings to a TargetFreeEasy
- 4K-th Score LeaderboardEasy
- 5Cable Splicing CostEasy
- 6Weakest SquadsEasy
- 7Shrinking Treasure PilesEasy
- 8Almost Sorted RestoreEasy
- 9Min-Heap From ScratchEasy
- 10Most Common TagsMedium
- 11Nearest DronesMedium
- 12Cooldown SchedulerMedium
- 13Merge Sorted FeedsMedium
- 14Lightest K PairsMedium
- 15Single-Core Job RunnerMedium
- 16Rooftop RunnerMedium
- 17N-th Number Built From PrimesMedium
- 18Seat Reservation DeskMedium
- 19Fewest Conference RoomsMedium
- 20Exam Pass BoostMedium
- 21Best Crew ScoreMedium
- 22Server Task AssignmentMedium
- 23Hiring From Both EndsMedium
- 24Busiest RoomMedium
- 25Median BoardHard
- 26Seed Capital ProjectsHard
- 27Smallest Covering RangeHard
- 28Rainwater on TerrainHard
- 29Wage Group CostHard
- 30Window MediansHard
Solve them in the browser, in Python or JavaScript.