DSAJanuary 5, 20256 min read

Heap and Priority Queue: Solving Top-K Problems in Java

PriorityQueue in Java, max-heap vs min-heap, the K closest points trick, merge K sorted lists, and finding the Kth largest element — patterns and templates.

DSAJavaLeetCodeHeapPriority Queue

Java's PriorityQueue is a min-heap by default. Once you know how to flip it to a max-heap and when to use each, a whole category of 'top K' problems becomes easy.

Min-Heap vs Max-Heap in Java

HeapExamples.java
java
1// Min-heap (default)
2PriorityQueue<Integer> minHeap = new PriorityQueue<>();
3
4// Max-heap
5PriorityQueue<Integer> maxHeap = new PriorityQueue<>(Collections.reverseOrder());
6// or: new PriorityQueue<>((a, b) -> b - a)
7
8// Custom comparator — sort by frequency descending
9PriorityQueue<int[]> freqHeap = new PriorityQueue<>((a, b) -> b[1] - a[1]);

Kth Largest Element — Two Approaches

KthLargest.java
java
1// Approach 1: Min-heap of size K — O(n log k)
2// The top of a min-heap of size K is the Kth largest
3public int findKthLargest(int[] nums, int k) {
4    PriorityQueue<Integer> minHeap = new PriorityQueue<>();
5    for (int num : nums) {
6        minHeap.offer(num);
7        if (minHeap.size() > k) minHeap.poll(); // remove smallest
8    }
9    return minHeap.peek(); // Kth largest
10}
11
12// Approach 2: QuickSelect — O(n) average, O(n²) worst
13// Use when k is small relative to n and you can't afford extra space

Merge K Sorted Lists

MergeKSortedLists.java
java
1public ListNode mergeKLists(ListNode[] lists) {
2    // Min-heap ordered by node value
3    PriorityQueue<ListNode> heap = new PriorityQueue<>(
4        (a, b) -> a.val - b.val
5    );
6
7    // Add the head of each list
8    for (ListNode node : lists)
9        if (node != null) heap.offer(node);
10
11    ListNode dummy = new ListNode(0), curr = dummy;
12    while (!heap.isEmpty()) {
13        ListNode node = heap.poll();
14        curr.next = node;
15        curr = curr.next;
16        if (node.next != null) heap.offer(node.next);
17    }
18    return dummy.next;
19}

Top-K pattern: if K is much smaller than N, use a min-heap of size K (O(n log k)). If you need the full sorted top-K list, use a max-heap and pop K times (O(n + k log n)). QuickSelect is O(n) average but not stable.

Top K Frequent Elements

TopKFrequent.java
java
1public int[] topKFrequent(int[] nums, int k) {
2    Map<Integer, Integer> freq = new HashMap<>();
3    for (int n : nums) freq.merge(n, 1, Integer::sum);
4
5    // Min-heap of size k, ordered by frequency
6    PriorityQueue<Map.Entry<Integer, Integer>> heap =
7        new PriorityQueue<>(Comparator.comparingInt(Map.Entry::getValue));
8
9    for (Map.Entry<Integer, Integer> entry : freq.entrySet()) {
10        heap.offer(entry);
11        if (heap.size() > k) heap.poll(); // remove least frequent
12    }
13
14    return heap.stream().mapToInt(Map.Entry::getKey).toArray();
15}
16
17// O(n) bucket sort approach — when k is close to n
18public int[] topKFrequentBucket(int[] nums, int k) {
19    Map<Integer, Integer> freq = new HashMap<>();
20    for (int n : nums) freq.merge(n, 1, Integer::sum);
21
22    List<Integer>[] buckets = new List[nums.length + 1];
23    freq.forEach((num, f) -> {
24        if (buckets[f] == null) buckets[f] = new ArrayList<>();
25        buckets[f].add(num);
26    });
27
28    List<Integer> result = new ArrayList<>();
29    for (int i = buckets.length - 1; i >= 0 && result.size() < k; i--)
30        if (buckets[i] != null) result.addAll(buckets[i]);
31
32    return result.stream().mapToInt(Integer::intValue).toArray();
33}

Find Median from Data Stream

MedianFinder.java
java
1class MedianFinder {
2    // Two heaps: maxHeap (lower half) + minHeap (upper half)
3    PriorityQueue<Integer> lo = new PriorityQueue<>(Collections.reverseOrder()); // max-heap
4    PriorityQueue<Integer> hi = new PriorityQueue<>(); // min-heap
5
6    public void addNum(int num) {
7        lo.offer(num);
8        hi.offer(lo.poll()); // balance: push largest of lo into hi
9        if (lo.size() < hi.size()) lo.offer(hi.poll()); // lo always >= hi in size
10    }
11
12    public double findMedian() {
13        return lo.size() > hi.size()
14            ? lo.peek()
15            : (lo.peek() + hi.peek()) / 2.0;
16    }
17}

More in DSA