Heap
A heap is a complete binary tree stored as an array that gives O(log n) insertion and O(1) peek of the minimum (or maximum) — Java's PriorityQueue is a min-heap by default.
- Replaces O(n) repeated linear scans with O(log n) insertions and O(1) peek — critical when you need the extreme element repeatedly as data arrives
- Two variants: min-heap (smallest at top, default) and max-heap (largest at top, use
(a, b) -> b - acomparator) - Reach for it when the problem asks for the kth element, the top-k items, merging sorted sequences, or tracking a running median
Quick Revision - Must Do Problems
If short on time, solve only these. They cover ~90% of heap patterns.
| # | Problem | Pattern | Why It's Essential | Time to Solve |
|---|---|---|---|---|
| 1 | Kth Largest Element | Bounded Min-Heap | Core pattern — min-heap of size k keeps k largest | 10 min |
| 2 | Top K Frequent Elements [B75-347] | Bounded Min-Heap | Extends core pattern to frequency comparison | 15 min |
| 4 | Meeting Rooms II | Interval + Heap | Sort by start, heap tracks end times — very common | 20 min |
| 7 | Merge K Sorted Lists [B75-23] | K-way Merge | Always pick global min from k sources | 20 min |
| 8 | Find Median from Data Stream [B75-295] | Two-Heap | Two-heap invariant — hardest heap pattern | 25 min |
Practice Tips
How to Identify a Heap Problem
- Problem asks for kth largest, kth smallest, or top k by some criterion
- You need to repeatedly access the minimum or maximum as new elements arrive
- Multiple sorted sequences need to be merged (k-way merge)
- Intervals: need to track the "earliest finishing" event at each step
- Running calculation (median, rank) on a stream of incoming numbers
Heap vs. Other Techniques
| If you see... | Use... | Not... |
|---|---|---|
| Kth largest/smallest | Heap (size k) | Sort (O(n log n) vs O(n log k)) |
| Top k by frequency or distance | Heap (size k) | Full sort (heap is more efficient) |
| Merge k sorted sequences | Heap (k-way merge) | Repeated pairwise merge (O(nk) vs O(n log k)) |
| Running median on stream | Two heaps | Sort (can't sort a stream efficiently) |
| Shortest path (weighted graph) | Dijkstra (heap) | BFS (BFS only works for unweighted) |
Interview Approach
- Identify which heap pattern: bounded size-k heap, k-way merge, two-heap, or interval scheduling
- Choose heap direction: min-heap keeps k largest (evict minimum); max-heap keeps k smallest (evict maximum)
- Write the comparator explicitly —
(a, b) -> a - bis min,(a, b) -> b - ais max - For custom objects (arrays, nodes), comparator accesses the right field:
(a, b) -> a[0] - b[0] - Always check
heap.isEmpty()beforepeek()orpoll()
4 Main Patterns
Pattern 1: Bounded Heap (Top K)
// Min-heap of size k keeps the k LARGEST elements (evict the smallest)
PriorityQueue<Integer> minHeap = new PriorityQueue<>();
for (int num : nums) {
minHeap.offer(num);
if (minHeap.size() > k) {
minHeap.poll(); // evict smallest
}
}
// minHeap.peek() = kth largest
// Max-heap of size k keeps the k SMALLEST elements (evict the largest)
PriorityQueue<Integer> maxHeap = new PriorityQueue<>((a, b) -> b - a);
for (int num : nums) {
maxHeap.offer(num);
if (maxHeap.size() > k) {
maxHeap.poll(); // evict largest
}
}
// maxHeap.peek() = kth smallest
Pattern 2: K-way Merge
// Heap holds one element per source; always extract the global min
// [value, source_index, position_in_source]
PriorityQueue<int[]> heap = new PriorityQueue<>((a, b) -> a[0] - b[0]);
// seed: add first element of each source
while (!heap.isEmpty()) {
int[] curr = heap.poll(); // global minimum
// add next element from the same source
if (hasNext(curr)) {
heap.offer(next(curr));
}
}
Pattern 3: Two-Heap for Median
// small = max-heap (lower half), large = min-heap (upper half)
// Invariant: small.size() == large.size() or small.size() == large.size() + 1
PriorityQueue<Integer> small = new PriorityQueue<>((a, b) -> b - a); // max-heap
PriorityQueue<Integer> large = new PriorityQueue<>(); // min-heap
// addNum: always add to small first, then cross-check, then rebalance
small.offer(num);
large.offer(small.poll()); // ensure small's max <= large's min
if (small.size() < large.size()) {
small.offer(large.poll()); // rebalance
}
Pattern 4: Interval Scheduling
// Sort by start time; heap tracks end times of active/assigned intervals
Arrays.sort(intervals, (a, b) -> a[0] - b[0]);
PriorityQueue<Integer> heap = new PriorityQueue<>(); // min-heap of end times
for (int[] interval : intervals) {
if (!heap.isEmpty() && heap.peek() <= interval[0]) {
heap.poll(); // reuse: earliest-ending is now free
}
heap.offer(interval[1]); // assign this interval
}
// heap.size() = number of rooms/workers/resources needed
General Templates
Java PriorityQueue Quick Reference
// Min-heap (default — smallest element at top)
PriorityQueue<Integer> minHeap = new PriorityQueue<>();
// Max-heap (largest element at top)
PriorityQueue<Integer> maxHeap = new PriorityQueue<>((a, b) -> b - a);
// Custom: sort int[] by first element ascending
PriorityQueue<int[]> heap = new PriorityQueue<>((a, b) -> a[0] - b[0]);
heap.offer(x); // insert — O(log n)
heap.poll(); // remove and return top — O(log n)
heap.peek(); // view top without removing — O(1)
heap.size(); // number of elements — O(1)
Problems in this guide
Problem 1: Kth Largest Element in an Array ⭐ MUST DO
| Difficulty | Time to Solve | Pattern |
|---|---|---|
| Medium | 10-15 min | Bounded Min-Heap |
Find the kth largest element in an unsorted array.
Example:
Input: nums = [3,2,1,5,6,4], k = 2 → 5
Input: nums = [3,2,3,1,2,4,5,5,6], k = 4 → 4
Key Insight:
- Use a min-heap of size k (counterintuitive: min not max). It holds the k largest elements seen so far. The heap's minimum is the kth largest — anything smaller than the heap's minimum cannot be in the top k, so evict it. After the loop, peek is the answer.
Solution:
public int findKthLargest(int[] nums, int k) {
PriorityQueue<Integer> minHeap = new PriorityQueue<>();
for (int num : nums) {
minHeap.offer(num);
if (minHeap.size() > k) {
minHeap.poll(); // evict smallest
}
}
return minHeap.peek(); // kth largest
}
Complexity:
| Time | O(n log k) — n inserts, each O(log k) |
| Space | O(k) — heap holds at most k+1 elements |
Real World: Recommendation engines rank items by a score (click-through rate, rating, relevance) and only need the top k results — not a full sort. A size-k min-heap processes the full catalogue in O(n log k) and keeps memory bounded at k regardless of catalogue size. Same pattern in log analysis: find the k slowest requests from millions of log lines.
Variations:
- Kth largest in a stream — same heap but elements arrive one at a time; maintain the heap as a running state rather than processing a full array at once.
- Kth smallest — flip to a max-heap of size k; evict the largest so the k smallest remain.
Interview Disguises:
- "Given a list of server response times, find the 99th percentile latency without sorting the full list." — kth largest where k = 1% of n; min-heap of size k.
- "A leaderboard receives score updates continuously. At any point return the top 10 players." — bounded min-heap of size 10 maintained as a running state; same as kth largest in a stream.
Problem 2: Top K Frequent Elements ⭐ MUST DO [B75-347]
| Difficulty | Time to Solve | Pattern |
|---|---|---|
| Medium | 15-20 min | Bounded Min-Heap |
Return the k most frequent elements from an integer array.
Example:
Input: nums = [1,1,1,2,2,3], k = 2 → [1,2]
Input: nums = [1], k = 1 → [1]
Key Insight:
- Same bounded min-heap pattern as Kth Largest, but the comparison key is frequency, not value. Count frequencies in a HashMap, then apply the size-k min-heap with a comparator that compares by
count.get(a). The k most frequent remain in the heap.
Solution:
public int[] topKFrequent(int[] nums, int k) {
Map<Integer, Integer> count = new HashMap<>();
for (int num : nums) {
count.put(num, count.getOrDefault(num, 0) + 1);
}
PriorityQueue<Integer> minHeap = new PriorityQueue<>((a, b) -> count.get(a) - count.get(b));
for (int num : count.keySet()) {
minHeap.offer(num);
if (minHeap.size() > k) {
minHeap.poll();
}
}
int[] result = new int[k];
for (int i = 0; i < k; i++) {
result[i] = minHeap.poll();
}
return result;
}
Complexity:
| Time | O(n log k) — building map O(n), heap operations O(d log k) where d = distinct values |
| Space | O(n) — frequency map |
Real World: Analytics dashboards showing "top k trending hashtags" or "most visited pages" over a rolling time window. The frequency map is updated as events arrive; rebuilding the top-k heap periodically is O(d log k) not O(d log d). Same pattern in autocomplete: surface the k most frequently typed completions for a given prefix.
Variations:
- Top k by custom weight — replace the frequency comparator with any scoring function (recency-weighted count, TF-IDF score). The heap structure doesn't change, only the comparator.
- Bucket sort O(n) solution — create n+1 frequency buckets, place each element in its bucket, then scan from the highest bucket down until k elements are collected. O(n) time, O(n) space — beats the heap when k is close to n.
Interview Disguises:
- "Given server access logs, return the k most accessed API endpoints in the last hour." — frequency count by endpoint, then top-k min-heap by count.
- "Find the k words that appear most often in a document corpus. Memory is limited so you cannot sort all words." — top k frequent with bounded heap; O(n log k) and O(k) working memory.
Problem 3: K Closest Points to Origin
| Difficulty | Time to Solve | Pattern |
|---|---|---|
| Medium | 15-20 min | Bounded Max-Heap |
Return the k closest points to the origin (0, 0).
Example:
Input: points = [[1,3],[-2,2]], k = 1 → [[-2,2]]
Input: points = [[3,3],[5,-1],[-2,4]], k = 2 → [[3,3],[-2,4]]
Key Insight:
- To keep the k smallest distances, use a max-heap of size k and evict the farthest. No need to compute sqrt — comparing squared Euclidean distances is equivalent and avoids floating point. When
heap.size() > k, poll removes the farthest point (max-heap top).
Solution:
public int[][] kClosest(int[][] points, int k) {
PriorityQueue<int[]> maxHeap = new PriorityQueue<>(
(a, b) -> (b[0]*b[0] + b[1]*b[1]) - (a[0]*a[0] + a[1]*a[1])
);
for (int[] point : points) {
maxHeap.offer(point);
if (maxHeap.size() > k) {
maxHeap.poll(); // evict farthest
}
}
int[][] result = new int[k][2];
for (int i = 0; i < k; i++) {
result[i] = maxHeap.poll();
}
return result;
}
Complexity:
| Time | O(n log k) |
| Space | O(k) |
Real World: Ride-sharing dispatch: given GPS coordinates of all available drivers, find the k nearest to a pickup request. Squared Euclidean distance avoids the sqrt and gives the same ordering. Also used in k-nearest-neighbour classification and spatial indexing when a full sort of all points is too slow.
Variations:
- K closest by Manhattan distance — replace the Euclidean distance formula with
|x| + |y|; same max-heap structure, different comparator. - Closest point to a query point (not origin) — shift coordinates: replace
a[0]*a[0] + a[1]*a[1]with(a[0]-qx)*(a[0]-qx) + (a[1]-qy)*(a[1]-qy).
Interview Disguises:
- "Given a list of warehouses with coordinates, return the k closest to a distribution centre." — k closest points where the centre is the query point, not the origin.
- "A food delivery app needs to show the k nearest restaurants to a user's location." — same problem; use squared distance to avoid floating point; max-heap of size k.
Problem 4: Meeting Rooms II ⭐ MUST DO
| Difficulty | Time to Solve | Pattern |
|---|---|---|
| Medium | 20-25 min | Interval + Heap |
Given a list of meeting intervals [start, end], return the minimum number of conference rooms required.
Example:
Input: [[0,30],[5,10],[15,20]] → 2
Input: [[7,10],[2,4]] → 1
Key Insight:
- Sort meetings by start time. Use a min-heap of end times — one entry per room currently in use. For each new meeting, if the earliest-ending meeting has already finished (
heap.peek() <= start), that room is free: remove it and reuse it. Always add the new meeting's end time. The heap size after processing all meetings is the answer.
Solution:
public int minMeetingRooms(int[][] intervals) {
Arrays.sort(intervals, (a, b) -> a[0] - b[0]);
PriorityQueue<Integer> endTimes = new PriorityQueue<>();
for (int[] meeting : intervals) {
if (!endTimes.isEmpty() && endTimes.peek() <= meeting[0]) {
endTimes.poll(); // reuse room: earliest meeting just ended
}
endTimes.offer(meeting[1]); // assign room, track end time
}
return endTimes.size();
}
Complexity:
| Time | O(n log n) — sort + n heap operations |
| Space | O(n) — heap holds at most n end times |
Real World: Any resource allocation problem where jobs arrive with a start and end time and you need the minimum number of workers, threads, or servers: CI/CD pipeline job scheduling, cloud VM slot allocation, hospital bed assignment. The heap models resources currently in use; its size at any point is the current demand.
Variations:
- Maximum overlap at any instant — same algorithm; the answer is the peak heap size during the sweep, which equals the minimum rooms needed.
- Assign meetings to specific rooms — track which room (ID) is in the heap alongside its end time; on reuse, record the room assignment. Same heap, richer payload.
Interview Disguises:
- "Given a list of flight intervals, find the minimum number of airport gates required so no two flights share a gate simultaneously." — meeting rooms II with flights as intervals.
- "A streaming platform starts and stops video encodes. Find the peak number of concurrent encodes to size the worker pool." — same problem; intervals are encode start/end times.
Problem 5: Kth Smallest Element in a Sorted Matrix
| Difficulty | Time to Solve | Pattern |
|---|---|---|
| Medium | 20-25 min | K-way Merge |
Find the kth smallest element in an n×n matrix where each row and column is sorted in ascending order.
Example:
Input: matrix = [[1,5,9],[10,11,13],[12,13,15]], k = 8 → 13
Input: matrix = [[-5]], k = 1 → -5
Key Insight:
- Treat each row as a sorted list. Start the min-heap with the first element of each row (up to k rows). On each extraction, add the next element from the same row. After k extractions, the last extracted value is the answer — same as Merge K Sorted Lists.
Solution:
public int kthSmallest(int[][] matrix, int k) {
int n = matrix.length;
PriorityQueue<int[]> minHeap = new PriorityQueue<>((a, b) -> a[0] - b[0]);
for (int i = 0; i < Math.min(n, k); i++) {
minHeap.offer(new int[]{matrix[i][0], i, 0}); // [value, row, col]
}
int val = 0;
for (int i = 0; i < k; i++) {
int[] curr = minHeap.poll();
val = curr[0];
int row = curr[1], col = curr[2];
if (col + 1 < n) {
minHeap.offer(new int[]{matrix[row][col + 1], row, col + 1});
}
}
return val;
}
Complexity:
| Time | O(k log n) — k extractions, heap size ≤ n |
| Space | O(n) — heap |
Real World: Database query planners that merge results from multiple sorted index scans — each scan is a sorted sequence, the heap picks the globally smallest row at each step. Also used in external merge sort: when merging k sorted file chunks that don't fit in memory, a k-way heap merge reads the minimum across all chunks without loading them fully.
Variations:
- Kth smallest in k sorted arrays — same pattern; seed with the first element of each array, advance within the same array on each poll.
- Find the median of a sorted matrix — binary search on value combined with a count of elements ≤ mid; or k-way merge up to the n²/2 th element.
Interview Disguises:
- "You have k database shards each returning sorted result pages. Merge them into a single sorted stream without loading all results into memory." — k-way merge; heap holds the current front of each shard's page.
- "Given a sorted matrix, find the kth smallest sum of any row pair (one element from each row)." — variant; heap tracks candidate sums with indices to generate next candidates.
Problem 6: Task Scheduler
| Difficulty | Time to Solve | Pattern |
|---|---|---|
| Medium | 25-30 min | Max-Heap + Greedy |
Given a list of tasks and a cooldown period n, return the minimum time to execute all tasks. Identical tasks must be separated by at least n intervals.
Example:
Input: tasks = ["A","A","A","B","B","B"], n = 2 → 8 (A→B→idle→A→B→idle→A→B)
Input: tasks = ["A","A","A","B","B","B"], n = 0 → 6
Key Insight:
- Greedy: always execute the most frequent remaining task first. In each cooldown cycle of n+1 time slots, execute up to n+1 distinct tasks (most frequent first), then add idle slots if fewer than n+1 distinct tasks remain. Repeat until all tasks are done.
Solution:
public int leastInterval(char[] tasks, int n) {
int[] freq = new int[26];
for (char task : tasks) {
freq[task - 'A']++;
}
PriorityQueue<Integer> maxHeap = new PriorityQueue<>((a, b) -> b - a);
for (int f : freq) {
if (f > 0) maxHeap.offer(f);
}
int time = 0;
while (!maxHeap.isEmpty()) {
List<Integer> temp = new ArrayList<>();
for (int i = 0; i <= n; i++) { // one cooldown cycle: n+1 slots
if (!maxHeap.isEmpty()) {
int f = maxHeap.poll();
if (f > 1) {
temp.add(f - 1);
}
}
time++;
if (maxHeap.isEmpty() && temp.isEmpty()) {
break; // no more tasks
}
}
for (int f : temp) {
maxHeap.offer(f);
}
}
return time;
}
Complexity:
| Time | O(t × log 26) = O(t) where t = total tasks — 26 distinct task types at most |
| Space | O(26) = O(1) — fixed frequency array + bounded heap |
Real World: OS CPU schedulers balance task fairness with throughput — a cooldown prevents the same process from monopolising the CPU. The "most frequent first" greedy minimises total time, which maps to minimising context switches. Also appears in rate-limited API call scheduling: if the same endpoint can only be called once every n seconds, schedule the most-needed calls first.
Variations:
- Math formula O(1) — the answer is
max(totalTasks, (maxFreq - 1) * (n + 1) + countOfMaxFreq). The heap solution is easier to derive under pressure; the formula is the follow-up once the greedy logic is clear. - Tasks with dependencies — if task B must run after task A, this becomes a topological sort problem (see Graphs). The cooldown constraint alone is handled by the heap approach.
Interview Disguises:
- "A rate limiter allows each API endpoint to be called at most once every n seconds. Given a sequence of requests, find the minimum total time to process all of them." — task scheduler with endpoints as task types and n as the cooldown.
- "A print queue has jobs of k types. Each printer needs a cooldown of n jobs between same-type prints. Minimise total print time." — identical structure; job types map to task characters.
Problem 7: Merge K Sorted Lists ⭐ MUST DO [B75-23]
| Difficulty | Time to Solve | Pattern |
|---|---|---|
| Hard | 20-25 min | K-way Merge |
Merge k sorted linked lists into one sorted list.
Example:
Input: [[1,4,5],[1,3,4],[2,6]] → [1,1,2,3,4,4,5,6]
Key Insight:
- Seed the min-heap with the first node of each non-null list. Always extract the global minimum, append it to the result, then add its next node (if any) back to the heap. At most k nodes are in the heap at any time.
Solution:
public ListNode mergeKLists(ListNode[] lists) {
PriorityQueue<ListNode> minHeap = new PriorityQueue<>((a, b) -> a.val - b.val);
for (ListNode node : lists) {
if (node != null) minHeap.offer(node);
}
ListNode dummy = new ListNode(0), curr = dummy;
while (!minHeap.isEmpty()) {
ListNode node = minHeap.poll();
curr.next = node;
curr = curr.next;
if (node.next != null) {
minHeap.offer(node.next);
}
}
return dummy.next;
}
Complexity:
| Time | O(n log k) — n total nodes, each inserted and removed once; heap size ≤ k |
| Space | O(k) — heap holds one node per list |
Real World: Distributed databases merge sorted results from k shards into a single sorted response — each shard returns a sorted page and the coordinator uses a k-way heap merge. Also the final step of external merge sort: k sorted temporary files are merged into one output file by always writing the smallest available record.
Variations:
- Merge k sorted arrays — same algorithm; store
[value, arrayIndex, elementIndex]in the heap instead of list nodes. - Find the smallest range covering at least one element from each list — extend k-way merge: track the current max across all heap entries alongside the min (heap top); the range is
[min, max]; advance the list containing the current min and update max.
Interview Disguises:
- "You have k microservices each producing a sorted stream of timestamped events. Merge them into a single chronological stream." — merge k sorted lists where nodes are events sorted by timestamp.
- "Given k sorted log files, produce a single merged log sorted by timestamp for ingestion into a data warehouse." — same structure; each file is a sorted list, heap picks the earliest timestamp at each step.
Problem 8: Find Median from Data Stream ⭐ MUST DO [B75-295]
| Difficulty | Time to Solve | Pattern |
|---|---|---|
| Hard | 25-30 min | Two-Heap |
Design a data structure that supports adding integers and finding the median at any time.
Example:
addNum(1) → addNum(2) → findMedian() → 1.5
addNum(3) → findMedian() → 2.0
Key Insight:
- Maintain two heaps:
small(max-heap) holds the lower half,large(min-heap) holds the upper half. Invariant:small.size() == large.size()(even count, median = average of tops) orsmall.size() == large.size() + 1(odd count, median = small.peek()). - Add to
smallfirst, then movesmall's max tolargeto ensure the split is valid, then rebalance iflargegrew larger thansmall.
Solution:
class MedianFinder {
private PriorityQueue<Integer> small; // max-heap: lower half
private PriorityQueue<Integer> large; // min-heap: upper half
public MedianFinder() {
small = new PriorityQueue<>((a, b) -> b - a);
large = new PriorityQueue<>();
}
public void addNum(int num) {
small.offer(num);
large.offer(small.poll()); // ensure small's max ≤ large's min
if (small.size() < large.size()) {
small.offer(large.poll()); // rebalance: small must be >= large
}
}
public double findMedian() {
return small.size() > large.size()
? small.peek()
: (small.peek() + large.peek()) / 2.0;
}
}
Complexity:
| Time | O(log n) per addNum, O(1) per findMedian |
| Space | O(n) — both heaps combined hold all elements |
Real World: Real-time analytics systems that report the median latency or median transaction value over a sliding window of incoming events — you can't sort a live stream, so the two-heap invariant gives O(1) median at any instant. Used in monitoring dashboards, fraud detection score tracking, and A/B test metric aggregation where the median is more robust than the mean.
Variations:
- Sliding window median — same two-heap structure but elements also leave the window; requires a lazy deletion map to mark removed elements and skip them on poll. Significantly harder — a common hard follow-up.
- Kth percentile instead of median — adjust the size invariant: instead of keeping the heaps balanced at 50/50, maintain
small.size() = k% of totalandlarge.size() = (100-k)% of total. The kth percentile is alwayssmall.peek().
Interview Disguises:
- "A trading system receives a stream of stock prices. After each price, report the median price seen so far to detect if the market is drifting." — find median from data stream; each tick is an
addNumcall. - "Monitor API response times in real time. At any moment, return the median response time without storing the full history sorted." — same structure; each response time is a data point added to the two-heap.
Quick Reference Table
| # | Problem | Pattern | Key Technique | Time |
|---|---|---|---|---|
| 1 | Kth Largest Element ⭐ | Bounded Min-Heap | Min-heap size k, peek = kth largest | O(n log k) |
| 2 | Top K Frequent ⭐ [B75-347] | Bounded Min-Heap | Compare by count.get(), size k | O(n log k) |
| 3 | K Closest Points | Bounded Max-Heap | Max-heap size k, compare squared dist | O(n log k) |
| 4 | Meeting Rooms II ⭐ | Interval + Heap | Sort by start, heap of end times | O(n log n) |
| 5 | Kth Smallest Matrix | K-way Merge | Heap of [val, row, col], advance col | O(k log n) |
| 6 | Task Scheduler | Max-Heap + Greedy | n+1 cycle, most frequent first | O(n) |
| 7 | Merge K Sorted Lists ⭐ [B75-23] | K-way Merge | Seed with list heads, advance on poll | O(n log k) |
| 8 | Find Median ⭐ [B75-295] | Two-Heap | small(max) + large(min), balanced halves | O(log n) |
When to Use Each Pattern
Bounded Heap (Top K)
- "Return the k largest/smallest/most frequent/closest" — fixed k, process all n elements
- Min-heap of size k keeps k largest; max-heap of size k keeps k smallest
- After the loop, drain the heap into the result array
K-way Merge
- Multiple sorted sequences (lists, arrays, matrix rows) need to be merged
- Seed heap with the first element of each source (with source pointer)
- On each poll, add the next element from that same source
- Stops when heap is empty (all sources exhausted)
Two-Heap for Median
- Running median on a stream — need O(1) access to both halves of the data
small(max-heap) = lower half,large(min-heap) = upper half- Invariant:
small.size()≥large.size()andsmall.peek()≤large.peek() - Median =
small.peek()if sizes differ, else average of both tops
Interval Scheduling
- Sort intervals by start time; need to match "available" resources to new intervals
- Min-heap of end times tracks when each resource next becomes free
- On each new interval: if
heap.peek() <= start, reclaim that resource (poll), then push new end time - Final heap size = number of resources (rooms, workers) required
Common Mistakes to Avoid
General Heap Mistakes
- Wrong heap direction for top-k — to keep k largest, use a MIN-heap (not max): you evict the smallest of the k+1 candidates; similarly, to keep k smallest, use a MAX-heap
- Not copying result from heap — drain the heap at the end with
poll(), notpeek();peek()only reads the top - Integer overflow in comparator —
(a, b) -> b - aoverflows when values span the full int range; useInteger.compare(b, a)if values could be nearInteger.MIN_VALUE
Bounded Heap Mistakes
- Calling peek on empty heap — always check
!heap.isEmpty()before callingpeek()orpoll(); PriorityQueue throwsNoSuchElementExceptionon empty peek/poll
Two-Heap Mistakes
- Wrong rebalance condition —
smallmust never be smaller thanlarge; the condition isif (small.size() < large.size()) small.offer(large.poll()); reversing this breaks the median formula - Skipping the cross-check on add — adding to
smalland immediately rebalancing without movingsmall's max tolargefirst can violate the invariant thatsmall.peek() ≤ large.peek()
K-way Merge Mistakes
- Not seeding null-safe — check
if (node != null)before adding to the heap; null list heads cause NullPointerException in the comparator - Forgetting to advance the pointer — after polling a node, add
node.next(not a fresh reference) back to the heap; each extracted node must be followed by its successor or nothing