Mid-level 3 min · March 06, 2026

Heap Interview Problems — Unbounded Heap Freezes Dashboard

Real-time dashboard froze for 30s due to an unbounded heap — fix: always cap size for top K problems.

N
Naren · Founder
Plain-English first. Then code. Then the interview question.
About
 ● Production Incident 🔎 Debug Guide
Quick Answer
  • Heap is a complete binary tree that gives O(1) access to the min or max element.
  • Two patterns dominate: Top-K (min-heap for largest K) and Two-Heaps (median in stream).
  • Building a heap from an array costs O(N), not O(N log N). Most engineers miss this.
  • In production, a heap's O(N) search cost kills you – don't use it for lookups.
  • Biggest mistake: using a max-heap for "K largest" – you need a min-heap to evict the smallest.
Plain-English First

Imagine a hospital emergency room. Patients don't get seen in the order they arrive — the sickest person always jumps to the front of the queue. A heap is that exact system for your data: it's a special structure that always keeps the 'most important' item (biggest or smallest) instantly grabbable at the top, no matter how many items you add or remove. That instant access is the whole magic.

If there’s one data structure that separates candidates who “know algorithms” from the ones who actually think like engineers under pressure, it’s the heap. I still remember my first FAANG onsite — the interviewer threw “merge K sorted lists” at me and smiled when I reached for a sorted array. Within 30 seconds I was sweating because I knew the time complexity was going to explode. That day I learned heaps the hard way.

Heaps shine exactly where the dataset is constantly changing and you need lightning-fast access to the current “extreme” value (smallest task, largest score, next event, etc.). Arrays and sorted lists technically work but they punish you with O(n) shifts or full re-sorts. Heaps keep that cost at O(log n), which is the difference between “Accepted” on LeetCode and “TLE on 10^6 input” in real production code.

The real skill isn’t memorizing code — it’s spotting when a problem secretly screams “I need a living priority queue.” By the end of this guide you’ll be able to instantly recognise heap patterns, implement the three canonical ones (Top-K, Two-Heaps, Merge-K) with zero hesitation, dodge the sneaky pitfalls that even 3-year-experienced devs fall into, and explain your choices confidently when the interviewer starts drilling “What if K is 1? What if duplicates exist? What if memory is tight?”

Top K Elements: The Foundation of Heap Problems

This pattern shows up so often that I now treat “K largest / K smallest / K frequent / K closest” as an immediate heap signal. The trick that trips almost everyone the first time: for ‘K largest’ you actually use a Min-Heap of size K. Why? Because you want to keep the biggest candidates inside the heap and quickly kick out the smallest one when a better candidate arrives. Keeping the heap capped at exactly K gives you O(N log K) instead of O(N log N) — huge win on massive inputs.

I’ve used this exact pattern in production to keep only the top 50 trending hashtags out of millions of events per second.

KthLargestFinder.javaJAVA
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
package io.thecodeforge;

import java.util.PriorityQueue;

/**
 * Implementation of the Top K pattern.
 * Time Complexity: O(N log K)
 * Space Complexity: O(K)
 * This is the exact template I copy-paste in every interview now.
 */
public class KthLargestFinder {
    public int findKthLargest(int[] nums, int k) {
        // Min-Heap to keep the largest elements at the bottom
        // and the smallest of the 'Top K' at the peek.
        PriorityQueue<Integer> minHeap = new PriorityQueue<>();

        for (int num : nums) {
            minHeap.add(num);
            // If heap exceeds size K, remove the smallest element
            if (minHeap.size() > k) {
                minHeap.poll();
            }
        }

        // The top of the heap is the K-th largest element
        return minHeap.peek();
    }

    public static void main(String[] args) {
        KthLargestFinder finder = new KthLargestFinder();
        int[] stream = {3, 2, 3, 1, 2, 4, 5, 5, 6};
        int k = 4;
        System.out.println("The " + k + "th largest element is: " + finder.findKthLargest(stream, k));
    }
}
Output
The 4th largest element is: 4
Forge Tip: The 'Inverse' Rule
For 'K largest' problems, use a Min-Heap. For 'K smallest' problems, use a Max-Heap. This ensures that the root of your heap always represents the 'boundary' element you need to compare against. I draw this rule on the whiteboard in every mock interview — it has saved me more than once.
Production Insight
In production, capping heap size is non-negotiable.
Without a limit, the heap grows with every event — memory O(N) and time O(N log N).
Always enforce a max size when you only need the top K.
Key Takeaway
Top K pattern = Min-Heap of size K.
Keep the smallest of the largest candidates at the top.
If you want K largest, you evict the smallest — that's the min-heap job.

The Two Heaps Pattern: Finding Median in a Stream

This is the classic LeetCode 295 “Median from Data Stream” — Hard for a reason. Keeping a sorted list would be O(N) per insertion. The beautiful trick is maintaining two heaps that together always represent the sorted stream: a Max-Heap for the lower half and a Min-Heap for the upper half. The tops of these two heaps are always the candidates for the median. Balancing them after every insertion is the only tricky part, but once you internalise the size rule (left heap can have at most one extra element), it becomes second nature.

I’ve implemented this exact pattern in real-time analytics dashboards where median response time must be updated on every new request.

MedianFinder.javaJAVA
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
package io.thecodeforge;

import java.util.Collections;
import java.util.PriorityQueue;

public class MedianFinder {
    private PriorityQueue<Integer> small = new PriorityQueue<>(Collections.reverseOrder()); // Max-heap
    private PriorityQueue<Integer> large = new PriorityQueue<>(); // Min-heap

    public void addNum(int num) {
        // Add to max-heap, then move largest of 'small' to 'large'
        small.offer(num);
        large.offer(small.poll());

        // Keep heaps balanced: small can have at most 1 more than large
        if (small.size() < large.size()) {
            small.offer(large.poll());
        }
    }

    public double findMedian() {
        if (small.size() > large.size()) return small.peek();
        return (small.peek() + large.peek()) / 2.0;
    }
}
Output
// After adding [1, 2, 3]
// small: [2, 1], large: [3]
// Median: 2.0
Pitfall: Integer Overflow
When calculating the median of two integers, (a + b) / 2.0 is safer than (a + b) / 2 to avoid integer division, but in some languages, a + b can overflow. Always use double-precision math for the final step. I once lost points in an interview because I forgot this on negative numbers.
Production Insight
Real-time median tracking with two heaps works well for moderate streams.
For billions of items, the memory O(N) becomes a problem – consider quantile sketches like T-Digest.
The balancing logic is the most common source of off-by-one errors.
Key Takeaway
Two heaps keep the median candidates always at the top.
Balance after every insert: left heap can have at most one extra.
Median = (left.peek + right.peek) / 2.0 — always with double.

Heapify: Building a Heap in O(N) Time

Most engineers think building a heap from an unsorted array takes O(N log N) because they imagine inserting each element one by one. But smart people – and your interviewer – know that a bottom-up approach called heapify builds the heap in O(N). The trick is to start from the last non-leaf node and sift down. Why O(N)? The math works out: most nodes are near the leaves and need only trivial swaps.

I once got stuck in an interview because I couldn't explain the proof. Don't be me. Learn the artihmetic: number of nodes at height h is N/2^(h+1), each takes O(h) work. Summation gives O(N).

HeapifyExample.javaJAVA
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
package io.thecodeforge;

// Building a min-heap from array using bottom-up heapify
public class HeapifyExample {
    public static void heapify(int[] arr) {
        int n = arr.length;
        // Start from last non-leaf node
        for (int i = n / 2 - 1; i >= 0; i--) {
            siftDown(arr, n, i);
        }
    }

    private static void siftDown(int[] arr, int n, int i) {
        int smallest = i;
        int left = 2 * i + 1;
        int right = 2 * i + 2;

        if (left < n && arr[left] < arr[smallest]) smallest = left;
        if (right < n && arr[right] < arr[smallest]) smallest = right;

        if (smallest != i) {
            int swap = arr[i];
            arr[i] = arr[smallest];
            arr[smallest] = swap;
            siftDown(arr, n, smallest);
        }
    }

    public static void main(String[] args) {
        int[] arr = {3, 1, 6, 5, 2, 4};
        heapify(arr);
        // arr is now a valid min-heap: [1, 2, 4, 5, 3, 6]
    }
}
Output
// Before: [3, 1, 6, 5, 2, 4]
// After heapify (min-heap): [1, 2, 4, 5, 3, 6]
Why Heapify is O(N)
  • Most nodes (leaves) are at height 0 — they cost nothing.
  • Only a small fraction of nodes are high up and require deeper swaps.
  • The sum of heights across all nodes is ∼N – 1, resulting in O(N).
  • If you insert one by one, you pay log height for every node: O(N log N).
Production Insight
Using insertion to build a heap means O(N log N) startup cost.
In a latency-sensitive system, that extra factor kills you when N hits millions.
Heapify is not just an interview trick – it's a real production optimisation.
Key Takeaway
Building a heap from scratch costs O(N) with heapify.
Don't insert one by one — sift down from the middle.
Know the proof: sum of heights ≈ N.

Merge K Sorted Lists: The Classic Heap Application

This is the problem that humbled me in my first FAANG interview. Merge K sorted linked lists into one sorted list. A naive approach is to repeatedly scan all K heads to find the smallest — O(NK) where N is total elements. The heap solution: push all K heads into a min-heap, then repeatedly poll the smallest and push its next node. That's O(N log K). Huge gain when K is large.

I've used this pattern in a real system to merge log streams from multiple servers into a single sorted output.

MergeKSortedLists.javaJAVA
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
package io.thecodeforge;

import java.util.PriorityQueue;

public class MergeKSortedLists {
    public static class ListNode {
        int val;
        ListNode next;
        ListNode(int x) { val = x; }
    }

    public ListNode mergeKLists(ListNode[] lists) {
        PriorityQueue<ListNode> heap = new PriorityQueue<>((a, b) -> a.val - b.val);
        for (ListNode node : lists) {
            if (node != null) heap.offer(node);
        }

        ListNode dummy = new ListNode(0);
        ListNode tail = dummy;
        while (!heap.isEmpty()) {
            ListNode smallest = heap.poll();
            tail.next = smallest;
            tail = tail.next;
            if (smallest.next != null) {
                heap.offer(smallest.next);
            }
        }
        return dummy.next;
    }
}
Output
// Input: [[1,4,5],[1,3,4],[2,6]]
// Output: [1,1,2,3,4,4,5,6]
Alternative: Divide and Conquer Merge
You can also merge K lists by repeatedly merging pairs (divide and conquer). That gives O(N log K) as well, but with O(1) extra space. The heap approach uses O(K) space for the heap. Trade-off: heap is easier to write with custom objects; divide-and-conquer avoids extra allocations. Both are fine in interviews.
Production Insight
In production, the heap approach is simple but the heap overhead matters when K is huge.
For K in the thousands, the heap's O(log K) per poll starts showing.
Consider using a tournament tree (loser tree) for lower constant factors.
Key Takeaway
Merge K sorted lists = min-heap of K heads.
Poll smallest, push its next.
Complexity: O(N log K) time, O(K) space.

Sliding Window Maximum: Heap vs Deque

LeetCode 239: Find the maximum in every sliding window of size K. Heap solution: insert each element into a max-heap, but you need to lazily delete elements that are out of the window. This leads to O(N log N) worst-case. A smarter approach uses a monotonic deque giving O(N). The heap solution is elegant but the deque is the optimal interview answer. However, the heap variant tests your understanding of lazy deletion and tracking indices.

I've seen candidates try to use heap for this and struggle with stale elements. Know both.

SlidingWindowMaxHeap.javaJAVA
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
package io.thecodeforge;

import java.util.PriorityQueue;

public class SlidingWindowMaxHeap {
    public int[] maxSlidingWindow(int[] nums, int k) {
        // Max-heap storing pairs (value, index)
        PriorityQueue<int[]> heap = new PriorityQueue<>(
            (a, b) -> b[0] - a[0]
        );
        int n = nums.length;
        int[] res = new int[n - k + 1];
        for (int i = 0; i < n; i++) {
            heap.offer(new int[]{nums[i], i});
            // Remove elements outside the window
            while (heap.peek()[1] <= i - k) {
                heap.poll();
            }
            // Once the first window is complete, add max to result
            if (i >= k - 1) {
                res[i - k + 1] = heap.peek()[0];
            }
        }
        return res;
    }
}
Output
// Input: nums = [1,3,-1,-3,5,3,6,7], k = 3
// Output: [3,3,5,5,6,7]
Lazy Deletion Gotcha
When you poll stale elements, you must check the index, not the value. Values can repeat. Always store the index alongside the value. In the heap, you may have multiple stale copies – they'll be removed lazily when they reach the top.
Production Insight
The deque approach is O(N) and more efficient for real-time data streams.
Heaps with lazy deletion can cause unbounded memory growth if windows are large and values are all equal.
Prefer deque for production sliding window maximum.
Key Takeaway
Sliding window maximum can use heap + lazy deletion.
But a monotonic deque is optimal O(N).
If you use heap, store (value, index) and remove stale entries on poll.
● Production incidentPOST-MORTEMseverity: high

When a Heap Turned a Real-Time Dashboard Into a 30-Second Freeze

Symptom
Dashboard updates freeze for up to 30 seconds during high traffic, then slowly recover.
Assumption
We assumed keeping a sorted list of all events was fine because heap inserts are O(log n). With millions of inserts per second, log n doesn't save you when n is unbounded.
Root cause
The heap was never trimmed. For 'keep top 100' we kept adding all events to a max-heap without evicting. Memory and time grew linearly with event count.
Fix
Switch to a min-heap of fixed size K = 100. For each new event, add then poll if size > K. This bounds memory to O(K) and operations to O(N log K).
Key lesson
  • Always cap heap size when you only need the top K elements.
  • Log n looks cheap until n is unbounded.
  • State size limits out loud in interviews – it shows production awareness.
Production debug guideSymptom → Action guide for the most common heap blunders under the clock4 entries
Symptom · 01
Wrong extreme value returned (e.g., smallest instead of largest)
Fix
Check Comparator: Java's PriorityQueue is a min-heap. For max, use Collections.reverseOrder() or custom Comparator. Verify the order of compare(a,b): return positive if a should come after b in the heap.
Symptom · 02
Heap reports wrong size or contains outdated elements
Fix
Ensure you're not holding onto stale references. In dynamic problems (like sliding window), you need lazy deletion: keep a counter or timestamp and skip stale elements on poll.
Symptom · 03
Median is off by 0.5 after inserting first few numbers
Fix
Check balancing logic in Two-Heaps pattern. The max-heap (lower half) can have at most one extra element. Use if (small.size() > large.size() + 1) or the symmetric condition. Also verify division with doubles to avoid integer truncation.
Symptom · 04
Heap runs out of memory or becomes extremely slow for large inputs
Fix
You're probably not capping the heap size. For Top-K, size must be ≤ K. For stream processing, consider using a bounded heap. Also check if you accidentally built a heap from an array with insertion (O(N log N)) instead of heapify (O(N)).
★ Heap Quick-Fix Cheat SheetWhen you're mid-interview and your heap solution breaks, reach for these fixes.
Getting smallest instead of largest (or vice versa)
Immediate action
Swap the comparator direction immediately.
Commands
`new PriorityQueue<>(Collections.reverseOrder())`
`new PriorityQueue<>((a,b) -> b - a)`
Fix now
Use a min-heap for 'K largest', max-heap for 'K smallest'. This is the inverse rule.
Median calculation returns integer instead of double+
Immediate action
Cast to double before division.
Commands
`(small.peek() + large.peek()) / 2.0`
`(double)(small.peek() + large.peek()) / 2`
Fix now
Always use 2.0 or cast one operand to double to avoid integer truncation.
Heap poll returns elements in wrong order (custom object)+
Immediate action
Verify the compare method: return positive if first argument should come after second in heap order.
Commands
`@Override public int compare(Task t1, Task t2) { return Integer.compare(t1.priority, t2.priority); }`
`Comparator.comparingInt(Task::getPriority)`
Fix now
Write a test case: insert 3 values, poll all, assert order. That catches 99% of comparator bugs.
Kth largest returns wrong value for duplicate-heavy input+
Immediate action
Check if you're using strict inequality in comparator. Duplicates can cause heap to evict incorrectly.
Commands
Use `a - b` not `a > b`? Actually `a - b` can cause overflow. Prefer `Integer.compare(a, b)`.
`new PriorityQueue<>((a,b) -> Integer.compare(b, a))` for max-heap.
Fix now
Use Integer.compare instead of subtraction. If duplicates break your median logic, handle them explicitly: allow equal elements on either side.
Heap vs Other Data Structures
Data StructureAccess Extreme (Min/Max)Insert/DeleteSearch
Unsorted ArrayO(N)O(1) / O(N)O(N)
Sorted ArrayO(1)O(N)O(log N)
Binary Search TreeO(log N)O(log N)O(log N)
HeapO(1)O(log N)O(N)
When to pickOnly extreme mattersFrequent updatesRare lookups

Key takeaways

1
A heap is a specialized complete binary tree that satisfies the heap property
parent is always ≤ or ≥ children.
2
Heaps are the optimal choice for any problem requiring frequent access to the minimum or maximum element in a changing dataset.
3
The 'Two Heaps' pattern is the golden standard for dynamic median problems and balancing data segments.
4
PriorityQueue in Java is your go-to implementation, but remember to handle the Comparator logic carefully for custom objects
and always discuss stability in the interview.
5
Recognising the heap pattern early is what turns a 45-minute struggle into a clean 15-minute solution.

Common mistakes to avoid

4 patterns
×

Using a Max-Heap for 'Top K Largest'

Symptom
You keep the K largest? Actually you keep the K largest but you need to evict the smallest among them to maintain size K. A max-heap can't evict the smallest; you'd need to remove the max, which discards your best candidate. Result: wrong answer or O(N log N) with unbounded heap.
Fix
Use a Min-Heap of size K. For each element, add and then if size > K, poll the smallest. The heap root becomes the Kth largest.
×

Forgetting Java's PriorityQueue is a Min-Heap by default

Symptom
You write new PriorityQueue<>() and expect it to give the largest element on peek – but you get the smallest. Most common cause of getting the opposite answer.
Fix
Initialize with Collections.reverseOrder() for max-heap, or provide a custom Comparator.
×

Trying to search for a specific value in a heap

Symptom
You call contains() or iterate over the heap to find a value. That's O(N) time and defeats the purpose. Heaps are not designed for search.
Fix
Use a HashSet or BST if you need to find arbitrary elements. Mention this trade-off openly in interviews.
×

Ignoring space complexity – heap of size N for stream processing

Symptom
Your solution runs out of memory for large streams because you kept all elements in the heap instead of capping at K.
Fix
For Top-K, always cap heap size at K. Mention O(K) space in your answer. For Two-Heaps median, in worst case you still store all elements – note that it's O(N) space and consider alternatives like quantile sketches for production.
INTERVIEW PREP · PRACTICE MODE

Interview Questions on This Topic

Q01SENIOR
Explain how to merge $K$ sorted linked lists efficiently. What is the ti...
Q02SENIOR
LeetCode 239 (Hard): How would you use a heap to find the maximum in a s...
Q03SENIOR
Suppose you have a file so large it doesn't fit in memory. How would you...
Q04SENIOR
Why is a binary heap usually implemented using an array instead of a tre...
Q05SENIOR
Design a system that processes tasks with priorities and deadlines – how...
Q06SENIOR
What happens if two elements have the exact same priority? How do you ma...
Q01 of 06SENIOR

Explain how to merge $K$ sorted linked lists efficiently. What is the time complexity in terms of $N$ (total elements) and $K$?

ANSWER
Use a min-heap to store the head of each list. Repeatedly poll the smallest node, add to result, push its next node. Time: O(N log K). Space: O(K) for the heap.
FAQ · 5 QUESTIONS

Frequently Asked Questions

01
When should I use a Heap over a Binary Search Tree (BST)?
02
How do I implement a Max-Heap in Java?
03
What is the time complexity of building a heap?
04
Can a heap have duplicate values?
05
What's the difference between PriorityQueue and TreeSet in Java?
🔥

That's Coding Patterns. Mark it forged?

3 min read · try the examples if you haven't

Previous
Binary Search Interview Problems
13 / 17 · Coding Patterns
Next
Divide and Conquer Problems