Skip to main content

๐Ÿ”๏ธ Heap (Priority Queue)

Conceptโ€‹

A Heap is a complete binary tree satisfying the heap property:

  • Min-Heap: parent โ‰ค children โ†’ peek() gives the minimum
  • Max-Heap: parent โ‰ฅ children โ†’ peek() gives the maximum

Java's PriorityQueue<> is a min-heap by default.

OperationTime
InsertO(log n)
Peek (min/max)O(1)
Remove min/maxO(log n)
Build heap from n elementsO(n)

When to Useโ€‹

  • Find the k-th largest/smallest element
  • Always need quick access to current min or max
  • Merge k sorted lists/arrays
  • Scheduling problems (always process the next cheapest/fastest)
  • Running median (two heaps)

Java Templateโ€‹

// Min-Heap (default)
PriorityQueue<Integer> minHeap = new PriorityQueue<>();

// Max-Heap
PriorityQueue<Integer> maxHeap = new PriorityQueue<>(Collections.reverseOrder());

// Custom comparator: sort by second element descending
PriorityQueue<int[]> pq = new PriorityQueue<>((a, b) -> b[1] - a[1]);

// Common operations
pq.offer(element); // insert โ€” O(log n)
pq.poll(); // remove and return top โ€” O(log n)
pq.peek(); // view top without removing โ€” O(1)
pq.size();
pq.isEmpty();

Pattern: Top-K Elementsโ€‹

// K-th Largest Element in an Array
public int findKthLargest(int[] nums, int k) {
// Maintain a min-heap of size k
// The top of the heap is always the k-th largest seen so far
PriorityQueue<Integer> minHeap = new PriorityQueue<>();

for (int num : nums) {
minHeap.offer(num);
if (minHeap.size() > k) {
minHeap.poll(); // evict the smallest
}
}
return minHeap.peek(); // the k-th largest
}

Why min-heap for "top K largest"? Keep only the K largest. The smallest of those K is the k-th largest overall.


Worked Example: Find Median from Data Streamโ€‹

Maintain two heaps:

  • maxHeap (lower half) โ€” top is the largest small number
  • minHeap (upper half) โ€” top is the smallest large number
class MedianFinder {
private PriorityQueue<Integer> lower; // max-heap
private PriorityQueue<Integer> upper; // min-heap

public MedianFinder() {
lower = new PriorityQueue<>(Collections.reverseOrder());
upper = new PriorityQueue<>();
}

public void addNum(int num) {
lower.offer(num); // always add to lower first

// Balance: lower's max must be โ‰ค upper's min
if (!upper.isEmpty() && lower.peek() > upper.peek()) {
upper.offer(lower.poll());
}

// Keep sizes balanced (lower can have at most 1 extra)
if (lower.size() > upper.size() + 1) {
upper.offer(lower.poll());
} else if (upper.size() > lower.size()) {
lower.offer(upper.poll());
}
}

public double findMedian() {
if (lower.size() > upper.size()) return lower.peek();
return (lower.peek() + upper.peek()) / 2.0;
}
}

Worked Example 2: Task Schedulerโ€‹

public int leastInterval(char[] tasks, int n) {
int[] freq = new int[26];
for (char c : tasks) freq[c - 'A']++;

// Max-heap by frequency
PriorityQueue<Integer> maxHeap = new PriorityQueue<>(Collections.reverseOrder());
for (int f : freq) if (f > 0) maxHeap.offer(f);

// Queue of [remaining_count, available_at_time]
Queue<int[]> cooldown = new LinkedList<>();
int time = 0;

while (!maxHeap.isEmpty() || !cooldown.isEmpty()) {
time++;

if (!maxHeap.isEmpty()) {
int remaining = maxHeap.poll() - 1;
if (remaining > 0) {
cooldown.offer(new int[]{remaining, time + n});
}
}

if (!cooldown.isEmpty() && cooldown.peek()[1] == time) {
maxHeap.offer(cooldown.poll()[0]);
}
}
return time;
}

LeetCode Problemsโ€‹

๐ŸŸข Easyโ€‹

#ProblemPattern
703Kth Largest Element in a StreamMin-heap size k
1046Last Stone WeightMax-heap

๐ŸŸก Mediumโ€‹

#ProblemPattern
215Kth Largest Element in an ArrayMin-heap / quickselect
347Top K Frequent ElementsHeap + frequency map
373Find K Pairs with Smallest SumsMin-heap
378Kth Smallest in Sorted MatrixMin-heap / binary search
621Task SchedulerMax-heap + cooldown
973K Closest Points to OriginMax-heap size k

๐Ÿ”ด Hardโ€‹

#ProblemPattern
23Merge K Sorted ListsMin-heap
295Find Median from Data StreamTwo heaps
480Sliding Window MedianTwo heaps + window
632Smallest Range Covering Elements from K ListsMin-heap + max tracking