DSA Tutorial
🔍
Top 100 DSA ProblemsSoonBlind 75SoonNeetCode 150SoonPattern → Problem MapSoonEasy → Medium RoadmapSoonMedium → Hard RoadmapSoonFAANG Question BankSoonMock InterviewsSoon
Weekly Revision PlanSoonSpaced Repetition GuideSoonMixed Problem SetsSoonMock Interview ScheduleSoonSelf Assessment ChecklistSoon

Heap Pattern for Problem Solving

When the Answer Is "Whatever Is Best Right Now"

A heap (priority queue) gives you the smallest (or largest) item in O(1) and lets you insert or remove in O(log n). The Heap page explains how it works inside. This page is about which problems want one.

THE SIGNAL:  you repeatedly need the current best / worst / earliest / closest
             item, while items keep being ADDED or REMOVED.

  sorted array:  min in O(1), but insert costs O(n)
  plain array:   insert in O(1), but min costs O(n)
  heap:          min in O(1), insert and remove-min in O(log n)  ← balanced

The Four Recurring Shapes

1. TOP K              keep only the k best seen so far
                      → min-heap of size k for the LARGEST k
                      → max-heap of size k for the SMALLEST k

2. K-WAY MERGE        combine k sorted sources in order
                      → heap of each source's current front item

3. TWO HEAPS          balance two halves, read the middle
                      → max-heap (lower half) + min-heap (upper half)

4. EVENT SCHEDULING   process by time, track what's still running
                      → min-heap of end times / next free moments

Shape 1: Top k

scores = [42, 17, 88, 63, 5, 91, 30, 77], k = 3
min-heap of size 3 (root = weakest of the current top 3):

42          heap {42}
17          heap {17, 42}
88          heap {17, 42, 88}
63          push → {17, 42, 63, 88} → too big, pop 17 → {42, 63, 88}
 5          push → pop 5 immediately             → {42, 63, 88}
91          push → pop 42                        → {63, 88, 91}
30          push → pop 30                        → {63, 88, 91}
77          push → pop 63                        → {77, 88, 91}

Top 3: 77, 88, 91.   Time O(n log k), space O(k).

The counter-intuitive part: the k largest live in a min-heap, because what you constantly need to know is the smallest member — the one to evict.

Shape 2: k-Way Merge

lists:  [1, 7, 12]   [3, 4, 15]   [2, 9]   [5]

heap holds one front per list: {1, 3, 2, 5}
pop 1 (list 0) → push 7           {2, 3, 5, 7}
pop 2 (list 2) → push 9           {3, 5, 7, 9}
pop 3 (list 1) → push 4           {4, 5, 7, 9}
...
merged: 1 2 3 4 5 7 9 12 15

N items total, k lists → O(N log k)

Shape 3: Two Heaps for a Running Median

stream: 5, 15, 1, 3, 8, 7

LOW = max-heap (smaller half)   HIGH = min-heap (larger half)
rule: every LOW value ≤ every HIGH value, and |LOW| = |HIGH| or |HIGH| + 1

add 5   LOW {5}            HIGH {}          median 5.0
add 15  LOW {5}            HIGH {15}        median (5+15)/2 = 10.0
add 1   LOW {5, 1}         HIGH {15}        median 5.0
add 3   LOW {3, 1}         HIGH {5, 15}     median (3+5)/2 = 4.0
add 8   LOW {5, 3, 1}      HIGH {8, 15}     median 5.0
add 7   LOW {5, 3, 1}      HIGH {7, 8, 15}  median (5+7)/2 = 6.0

Each insert is O(log n), and the median is read from the roots in O(1).

Shape 4: How Many Rooms?

meetings [start, end): [9,10) [9,12) [10,11) [11,13) [12,14) [10,12)

sorted by start; heap = end times of meetings in progress
[9,10)   heap {10}                    rooms 1
[9,12)   10 > 9, can't reuse          {10, 12}          rooms 2
[10,11)  10 ≤ 10 → reuse (pop 10)     {11, 12}
[10,12)  11 > 10, can't reuse         {11, 12, 12}      rooms 3
[11,13)  11 ≤ 11 → reuse              {12, 12, 13}
[12,14)  12 ≤ 12 → reuse              {12, 13, 14}

Answer: 3 rooms (the most ever in use at once)

Code: All Four Shapes

import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.List; import java.util.PriorityQueue; public class HeapPatterns { // 1. Top k largest — MIN-heap of size k; its root is the weakest of the best static List<Integer> topK(int[] values, int k) { PriorityQueue<Integer> heap = new PriorityQueue<>(); for (int v : values) { heap.offer(v); if (heap.size() > k) heap.poll(); // drop the smallest of the k+1 } List<Integer> out = new ArrayList<>(heap); Collections.sort(out); return out; } // 2. k-way merge — heap holds {value, listIndex, position} of each list's front static List<Integer> mergeSortedLists(int[][] lists) { PriorityQueue<int[]> heap = new PriorityQueue<>((a, b) -> Integer.compare(a[0], b[0])); for (int i = 0; i < lists.length; i++) if (lists[i].length > 0) heap.offer(new int[]{lists[i][0], i, 0}); List<Integer> out = new ArrayList<>(); while (!heap.isEmpty()) { int[] top = heap.poll(); out.add(top[0]); int li = top[1], next = top[2] + 1; if (next < lists[li].length) heap.offer(new int[]{lists[li][next], li, next}); } return out; } // 3. Running median — max-heap for the lower half, min-heap for the upper half static List<Double> runningMedians(int[] stream) { PriorityQueue<Integer> low = new PriorityQueue<>(Collections.reverseOrder()); PriorityQueue<Integer> high = new PriorityQueue<>(); List<Double> medians = new ArrayList<>(); for (int x : stream) { if (low.isEmpty() || x <= low.peek()) low.offer(x); else high.offer(x); if (low.size() > high.size() + 1) high.offer(low.poll()); // rebalance else if (high.size() > low.size()) low.offer(high.poll()); medians.add(low.size() > high.size() ? low.peek() : (low.peek() + high.peek()) / 2.0); } return medians; } // 4. Rooms needed — min-heap of end times of meetings in progress static int roomsNeeded(int[][] meetings) { int[][] sorted = meetings.clone(); Arrays.sort(sorted, (a, b) -> a[0] != b[0] ? Integer.compare(a[0], b[0]) : Integer.compare(a[1], b[1])); PriorityQueue<Integer> ends = new PriorityQueue<>(); int most = 0; for (int[] m : sorted) { if (!ends.isEmpty() && ends.peek() <= m[0]) ends.poll(); // that room is free again ends.offer(m[1]); most = Math.max(most, ends.size()); } return most; } public static void main(String[] args) { int[] scores = {42, 17, 88, 63, 5, 91, 30, 77}; System.out.println("top 3 of " + Arrays.toString(scores) + ": " + topK(scores, 3)); System.out.println("merged: " + mergeSortedLists(new int[][]{{1, 7, 12}, {3, 4, 15}, {2, 9}, {5}})); StringBuilder sb = new StringBuilder("running medians:"); for (double m : runningMedians(new int[]{5, 15, 1, 3, 8, 7})) sb.append(String.format(" %.1f", m)); System.out.println(sb); int[][] meetings = {{9, 10}, {9, 12}, {10, 11}, {11, 13}, {12, 14}, {10, 12}}; System.out.println("rooms needed: " + roomsNeeded(meetings)); } }
Output:
top 3 of [42, 17, 88, 63, 5, 91, 30, 77]: [77, 88, 91]
merged: [1, 2, 3, 4, 5, 7, 9, 12, 15]
running medians: 5.0 10.0 5.0 4.0 5.0 6.0
rooms needed: 3

Heap or Something Else?

NeedBest tool
Top k once, all data availableHeap O(n log k), or quickselect O(n) average
Top k from a stream / huge dataHeap of size k — O(k) memory
k-th smallest repeatedly with insertsHeap(s)
Min/max of a sliding windowMonotonic deque (O(1) amortised) — see Monotonic Stack & Queue
Repeatedly the next closest / cheapest node in a graphHeap — the engine of Dijkstra
Everything in sorted order, onceJust sort
Remove arbitrary items, ordered iterationBalanced BST / TreeMap

How to Spot It

✓ "k largest / k smallest / k closest / k most frequent"
✓ "merge k sorted ..."
✓ "median of a stream" / "balance two halves"
✓ "at any moment, process the earliest-ending / cheapest / highest-priority item"
✓ A greedy rule that needs "the current best" again and again
  (joining ropes cheapest-first, scheduling by deadline, CPU task queues)

Complexity

ShapeTimeSpace
Top k of nO(n log k)O(k)
k-way merge of N itemsO(N log k)O(k)
Running median, n insertsO(n log n), O(1) per median readO(n)
Rooms for n meetingsO(n log n)O(n)

Common Mistakes

Using a max-heap for the k largest. It works only if you push everything (O(n) memory) and pop k times. The size-k min-heap is the streaming-friendly version.

Forgetting Python's heapq is a min-heap only. Push negated values (or (−priority, item) tuples) for max-heap behaviour.

Comparing tuples with uncomparable items. In Python, (value, list_obj) crashes on ties when lists can't be compared. Add a tie-breaker like the list index: (value, i, idx).

Not rebalancing both directions in the two-heap median — the sizes drift and the roots stop being the middle values.

Sorting meetings by end time for room counting. Rooms are assigned as meetings start, so process them by start time. Sorting by end (the right order for the greedy "most non-overlapping" problem) gives wrong room counts here.

Interview Questions

Q: Find the k most frequent words in a large list. What's your approach?

Count frequencies with a hash map, then keep a min-heap of size k ordered by (frequency, then reverse alphabetical for ties). For each word, push and pop if the heap exceeds k. Total O(n + u log k), where u is the number of distinct words. Finally, pop all and reverse to list the most frequent first.

Q: Why does the room-counting heap only need one pop per meeting?

Meetings are processed by start time. If the earliest-ending running meeting has finished, reusing its room keeps the heap size unchanged; if not, no meeting has finished and a new room is required. Other finished meetings remain in the heap, but they never cause an overcount — their rooms get reused by later meetings one at a time — so the maximum heap size still equals the true peak.

Q: How does the two-heap approach handle deleting values (e.g. a sliding-window median)?

Heaps don't support efficient arbitrary deletion, so use lazy deletion: record values to delete in a hash map, and discard them when they surface at a heap's root. Track the "logical" sizes separately for balancing. Each operation stays O(log n) amortised.

Summary

  • ›Reach for a heap when you repeatedly need the current best while items come and go.
  • ›Top k largest → min-heap of size k (evict the weakest).
  • ›k-way merge → heap of fronts, O(N log k).
  • ›Running median → two heaps, balanced within one element.
  • ›Scheduling → min-heap of end times; peak heap size = resources needed.

Next, Monotonic Stack & Queue keeps values in sorted order as they arrive, answering "next greater" and "sliding window maximum" questions in O(n) total.

Suggested Quiz

To keep the k LARGEST values from a stream, which heap and why?

1/5