Heap / Priority Queue

Medium
Time: O(log n) push/pop, O(n log k) for top KSpace: O(k) for top K, O(n) for median
0/13
problems solved
0%
1.

What is a Heap?

A heap is a complete binary tree that satisfies the heap property:

Min-Heap: Parent is always smaller than or equal to children. Root is the minimum.

Max-Heap: Parent is always larger than or equal to children. Root is the maximum.

Heaps are typically stored as arrays:

  • Parent of node at index i: (i-1)/2
  • Left child: 2i + 1
  • Right child: 2i + 2

Key operations:

  • Insert (push): O(log n) - add at end, bubble up
  • Extract min/max (pop): O(log n) - remove root, bubble down
  • Peek: O(1) - just return root

In Java, PriorityQueue is a min-heap by default. For max-heap, use Collections.reverseOrder() or negate values.

2.

The Counter-Intuitive K Largest Trick

To find the K largest elements, use a MIN-heap of size K. This seems backwards, but here's why it works:

  1. Keep a min-heap with at most K elements
  2. For each new number:
    • If heap has less than K elements, add it
    • Else if number > heap's minimum, remove min and add number
  3. The heap always contains the K largest seen so far
  4. The root (minimum of heap) is the Kth largest overall

Why min-heap? Because we want to efficiently remove the smallest of the K candidates. The smallest of the K largest is the Kth largest.

Similarly, for K smallest elements, use a MAX-heap of size K.

Code
Java

Interactive Kth Largest

Kth Largest Element

Use min-heap of size K - root is the Kth largest

k = 3
Finding the 3rd largest element
Speed:
Input array: [3, 2, 1, 5, 6, 4]
3
2
1
5
6
4
Min-Heap (size ≤ 3): Contains 3 largest seen so far
Empty - will store top 3 largest
Array representation:
0
Heap Size
-
Heap Min (Kth Largest)
0/6
Processed
Click Play to find the 3rd largest element

Key Insight:Use a MIN-heap of size K. The smallest element in the heap is the Kth largest overall. If a new number is larger than the heap's min, swap them.

3.

Top K Frequent Elements

A common variation: find the K most frequent elements. This combines frequency counting with the Top K pattern.

Approach 1: Heap (O(n log k))

  1. Count frequencies with a HashMap
  2. Use a min-heap of size K, ordered by frequency
  3. The heap contains the K most frequent elements

Approach 2: Bucket Sort (O(n))

  1. Count frequencies
  2. Create buckets where bucket[i] contains elements appearing i times
  3. Scan buckets from highest to lowest, collect K elements

Bucket sort is theoretically faster but uses more space. In interviews, both approaches are valid.

Code
Java

Interactive Kth Largest

Kth Largest Element

Use min-heap of size K - root is the Kth largest

k = 3
Finding the 3rd largest element
Speed:
Input array: [3, 2, 1, 5, 6, 4]
3
2
1
5
6
4
Min-Heap (size ≤ 3): Contains 3 largest seen so far
Empty - will store top 3 largest
Array representation:
0
Heap Size
-
Heap Min (Kth Largest)
0/6
Processed
Click Play to find the 3rd largest element

Key Insight:Use a MIN-heap of size K. The smallest element in the heap is the Kth largest overall. If a new number is larger than the heap's min, swap them.

4.

Two Heaps: Find Median from Data Stream

The median is the middle value when data is sorted. For a stream of numbers, we need to find the median efficiently after each insertion.

The key insight: split numbers into two halves using two heaps:

small (max-heap): Contains the smaller half. Root is the largest of the small numbers. large (min-heap): Contains the larger half. Root is the smallest of the large numbers.

Invariants:

  • All elements in small <= all elements in large
  • Size difference is at most 1
  • small.size >= large.size (small has the extra element if odd count)

Finding median:

  • If sizes equal: median = (small.root + large.root) / 2
  • If small has more: median = small.root

Adding a number:

  1. Add to small (max-heap)
  2. Move largest of small to large (rebalance)
  3. If large has more elements, move smallest of large back to small
Code
Java

Interactive Median Finder

Find Median from Data Stream

Two Heaps: maxHeap for left half, minHeap for right half

Speed:
Input stream: [2, 3, 4, 1, 5, 6]
2
3
4
1
5
6
MaxHeap (Left Half - Smaller)
Empty
Size: 0
MinHeap (Right Half - Larger)
Empty
Size: 0
Current Median
-
maxHeap.top: -
minHeap.top: -
0
Left Size
0
Right Size
0/6
Processed
Click Play to find running median

Key Insight: Keep two heaps balanced. MaxHeap stores smaller half, MinHeap stores larger half. Median is either maxHeap.top (odd count) or average of both tops (even count).

5.

Merge K Sorted Lists

Given K sorted linked lists, merge them into one sorted list. This is a classic heap problem.

Naive approach: Merge two at a time. O(Nk) where N is total elements.

Heap approach: O(N log k)

  1. Create a min-heap with the first node from each list
  2. Pop the smallest node, add it to result
  3. If that node has a next, push it to the heap
  4. Repeat until heap is empty

The heap always has at most K elements (one from each list), so operations are O(log k).

This pattern applies to any 'merge K sorted sequences' problem:

  • Merge K sorted arrays
  • Find smallest range covering elements from K lists
  • External sorting (merge sorted file chunks)
Code
Java

Interactive Merge K Lists

Merge K Sorted Lists

Use min-heap to always get the smallest available element

Speed:
K = 3 Sorted Lists:
1
1
4
5
2
1
3
4
3
2
6
Min-Heap (current front pointers):
Empty
Merged Result:
Elements will appear here...
0
Heap Size
0
Merged Count
8
Remaining
Click Play to merge K sorted lists

Key Insight: Keep a min-heap of size K (one element from each list). Extract min, add to result, then push the next element from that list. Time: O(N log K) where N is total elements.

6.

Task Scheduler

Given tasks with cooldown period n (same task must have n intervals between executions), find minimum time to complete all tasks.

This is a heap + greedy problem:

  1. Count frequency of each task
  2. Use a max-heap to always execute the most frequent available task
  3. Track tasks in cooldown in a queue with their ready time
  4. Each cycle: pop from heap, execute, add to cooldown queue if count > 0
  5. When cooldown expires, move task back to heap

Greedy insight: Always execute the most frequent task that's available. This minimizes idle time because frequent tasks are the bottleneck.

Alternative formula: If maxFreq is the highest frequency and maxCount is how many tasks have that frequency: result = max(tasks.length, (maxFreq - 1) * (n + 1) + maxCount)

Code
Java
7.

K Closest Points to Origin

Problem: Find K points closest to origin (0,0).

Approach: Use a MAX-heap of size K to keep the K smallest distances.

Why max-heap? Same logic as K largest, but inverted:

  • We want K smallest, so use max-heap
  • If new distance < heap's max, replace it
  • Root is the Kth smallest (furthest of the K closest)

Key insight: Don't need sqrt for distance comparison. Compare squared distances directly.

Example: points = [[1,3],[-2,2]], K = 1

dist([1,3]) = 1² + 3² = 10
dist([-2,2]) = 4 + 4 = 8
Max-heap with K=1:
- Add 10 (point [1,3])
- 8 < 10, so pop 10, add 8 (point [-2,2])
Result: [[-2,2]]
Code
Java
8.

Meeting Rooms II

Problem: Given meeting times, find minimum number of meeting rooms required.

Key Insight: A room becomes free when a meeting ends. Track end times in a min-heap.

Algorithm:

  1. Sort meetings by start time
  2. Min-heap stores end times of ongoing meetings
  3. For each meeting:
    • If it starts after earliest ending meeting, reuse that room (pop)
    • Add current meeting's end time to heap
  4. Heap size = number of concurrent meetings = rooms needed

Example: [[0,30],[5,10],[15,20]]

Sorted by start: [[0,30],[5,10],[15,20]]
[0,30]: heap = [30], rooms = 1
[5,10]: 5 < 30 (can't reuse), heap = [10,30], rooms = 2
[15,20]: 15 >= 10 (reuse!), pop 10, push 20, heap = [20,30], rooms = 2
Result: 2 rooms
Code
Java
9.

Reorganize String

Problem: Rearrange string so no two adjacent characters are the same. Return "" if impossible.

Greedy + Heap: Always place the most frequent character that's different from the last placed.

Algorithm:

  1. Count frequencies
  2. Max-heap ordered by frequency
  3. Each step: pop most frequent, append to result, hold it for next iteration
  4. Re-add previous character (if count > 0) to heap
  5. If heap empty but we have a held char with count > 0, impossible

Example: "aab"

Freq: {a:2, b:1}, heap = [(2,'a'), (1,'b')]
Step 1: pop 'a' (freq 2), result = "a", hold 'a' (freq 1)
Step 2: pop 'b' (freq 1), result = "ab", push back 'a', hold 'b' (freq 0)
Step 3: pop 'a' (freq 1), result = "aba"
Final: "aba"

Impossible check: If maxFreq > (n+1)/2, impossible (pigeonhole principle).

Code
Java
10.

IPO: Maximize Capital

Problem: Start with capital W, complete at most k projects. Each project needs capital to start and gives profit. Maximize final capital.

Greedy: Always pick the project with maximum profit among affordable ones.

Two Heaps Solution:

  1. Min-heap: projects sorted by required capital (to find affordable ones)
  2. Max-heap: affordable projects sorted by profit (to pick best)

Algorithm:

  1. Add all projects to min-heap by capital
  2. For k iterations:
    • Move all affordable projects (capital <= W) to max-heap
    • Pick project with max profit, add profit to W
    • If no affordable project exists, stop

Example: k=2, W=0, profits=[1,2,3], capital=[0,1,1]

Projects: (profit, capital) = (1,0), (2,1), (3,1)
Round 1: W=0, affordable: (1,0), pick it → W=1
Round 2: W=1, affordable: (2,1), (3,1), pick (3,1) → W=4
Final: 4
Code
Java
11.

Sliding Window Median

Problem: Find median for each sliding window of size k.

Challenge: Two Heaps work for streaming, but we also need to REMOVE elements as window slides.

Approach: Two heaps with lazy deletion.

Lazy Deletion:

  1. When element should be removed, mark it (add to hash map of "to delete")
  2. Before popping from heap, check if top should be deleted
  3. If yes, pop and decrement "to delete" count, repeat
  4. Only balance heaps after cleaning tops

Why lazy? Removing from middle of heap is O(n). Lazy deletion keeps it O(log n) per operation.

Balance Rule: After each operation, ensure |small.size - large.size| <= 1.

Code
Java

Interactive Median Finder

Find Median from Data Stream

Two Heaps: maxHeap for left half, minHeap for right half

Speed:
Input stream: [2, 3, 4, 1, 5, 6]
2
3
4
1
5
6
MaxHeap (Left Half - Smaller)
Empty
Size: 0
MinHeap (Right Half - Larger)
Empty
Size: 0
Current Median
-
maxHeap.top: -
minHeap.top: -
0
Left Size
0
Right Size
0/6
Processed
Click Play to find running median

Key Insight: Keep two heaps balanced. MaxHeap stores smaller half, MinHeap stores larger half. Median is either maxHeap.top (odd count) or average of both tops (even count).

12.

Common Edge Cases

Edge Cases to Handle:

1. Empty Heap Operations

if (!heap.isEmpty()) {
return heap.peek();
}
// Handle empty case

2. K larger than array size

if (k >= nums.length) {
// All elements are in top K
return Arrays.copyOf(nums, nums.length);
}

3. Single element

if (nums.length == 1) return nums[0];

4. All elements same

[5,5,5,5], k=2 → Any 2 elements work
Median stream [5,5,5] → 5 always

5. Negative numbers

// Distance calculations may need absolute value
int dist = Math.abs(point[0]) + Math.abs(point[1]); // Manhattan
int dist = point[0]*point[0] + point[1]*point[1]; // Euclidean squared

Common Mistakes:

MistakeConsequence
Wrong heap typeK largest needs min-heap
Forgetting to limit heap sizeO(n) space instead of O(k)
Empty heap peek/pollNullPointerException
Not handling duplicatesWrong frequency counts
Using sort() in JS for heapO(n log n) vs O(log n)
13.

When to Use Which Heap

Quick reference for heap problems:

ProblemHeap TypeSizeWhy
K largest elementsMin-heapKRoot is Kth largest
K smallest elementsMax-heapKRoot is Kth smallest
K closest pointsMax-heap by distKRoot is Kth closest
Top K frequentMin-heap by freqKKeep K highest frequencies
Streaming medianTwo heapsn/2 eachSplit at median
Merge K sortedMin-heapKAlways get global minimum
Task schedulerMax-heapvariesExecute most frequent first
Meeting rooms IIMin-heap of endsvariesFind earliest ending
Reorganize stringMax-heapvariesMost frequent available
IPOTwo heapsvariesAffordable + max profit

Decision Framework:

Need K largest/smallest? → Heap of size K
Need streaming median? → Two heaps
Merging sorted sequences? → Min-heap of K elements
Scheduling/ordering? → Max-heap by priority
Interval overlap count? → Min-heap of end times
14.

Complexity Analysis

Heap Operation Complexity:

OperationTimeSpace
Insert (offer)O(log n)O(1)
Extract (poll)O(log n)O(1)
PeekO(1)O(1)
Heapify arrayO(n)O(1)
SearchO(n)O(1)
Remove arbitraryO(n)O(1)

Problem-Specific Complexity:

ProblemTimeSpace
Kth largestO(n log k)O(k)
Top K frequentO(n log k)O(n + k)
Streaming medianO(n log n)O(n)
Merge K listsO(N log k)O(k)
Meeting rooms IIO(n log n)O(n)
Task schedulerO(n log 26) = O(n)O(26) = O(1)

Why O(n log k) is Better Than O(n log n):

For n = 1,000,000 and k = 100:
O(n log n) = 1,000,000 × 20 = 20,000,000 ops
O(n log k) = 1,000,000 × 7 = 7,000,000 ops
That's ~3x faster!

JavaScript Caveat: Using array.sort() for heap operations is O(n log n) per operation, not O(log n). For interviews, explain you'd use a proper heap implementation in production.

15.

Template Summary

Top K Template:

function topK(items, k, comparator):
heap = new Heap(opposite comparator) // Min for K largest
for item in items:
heap.add(item)
if heap.size > k:
heap.poll() // Remove "worst" of K+1
return heap.toArray() // Contains K best

Two Heaps Median Template:

class MedianFinder:
small = MaxHeap() // Lower half
large = MinHeap() // Upper half
add(num):
small.add(num)
large.add(small.poll()) // Balance via large
if large.size > small.size:
small.add(large.poll()) // Keep small >= large
median():
if small.size > large.size:
return small.peek()
return (small.peek() + large.peek()) / 2

Merge K Sorted Template:

function mergeK(lists):
heap = MinHeap() // (value, list index, element index)
for i, list in enumerate(lists):
if list not empty:
heap.add((list[0], i, 0))
result = []
while heap not empty:
val, listIdx, elemIdx = heap.poll()
result.add(val)
if elemIdx + 1 < lists[listIdx].length:
nextVal = lists[listIdx][elemIdx + 1]
heap.add((nextVal, listIdx, elemIdx + 1))
return result

Interview Tips:

  • State heap type explicitly (min vs max)
  • For K problems, explain the counter-intuitive choice
  • Draw the heap structure when debugging
  • In JS, acknowledge sort() inefficiency
  • Handle empty heap cases

Test Your Knowledge

Take a quick 15question quiz to test what you've learned.