Skip to content
intermediatePhase 13 · Java Collections

PriorityQueue (Heap)

Use PriorityQueue for min-heap and max-heap operations in DSA.

1h
4 problems
Topic Progress0%

Min-Heap

Min-Heap (Default PriorityQueue)

PriorityQueue in Java is a min-heap by default. The smallest element (based on natural ordering or Comparator) is always at the head.

Key characteristics:

  • Min-heap by default (smallest first)
  • O(log n) for offer/poll, O(1) for peek
  • Not synchronized
  • Does not allow null elements
  • Backed by a balanced binary heap (array)
import java.util.*;

public class MinHeapDemo {
    public static void main(String[] args) {
        // Default min-heap
        PriorityQueue<Integer> minHeap = new PriorityQueue<>();
        minHeap.offer(30);
        minHeap.offer(10);
        minHeap.offer(50);
        minHeap.offer(20);
        minHeap.offer(40);

        System.out.println("Min-heap (peek): " + minHeap.peek()); // 10
        System.out.println("Polling elements:");
        while (!minHeap.isEmpty()) {
            System.out.print(minHeap.poll() + " "); // 10 20 30 40 50
        }
        System.out.println();

        // Strings - natural ordering (alphabetical)
        PriorityQueue<String> names = new PriorityQueue<>();
        names.offer("Charlie");
        names.offer("Alice");
        names.offer("Bob");
        System.out.println("\nAlphabetical: " + names.peek()); // Alice
        while (!names.isEmpty()) {
            System.out.print(names.poll() + " ");
        }
        System.out.println();

        // Building from array
        int[] arr = {5, 3, 8, 1, 2, 9, 4};
        PriorityQueue<Integer> heap = new PriorityQueue<>();
        for (int num : arr) {
            heap.offer(num);
        }
        System.out.println("\nSorted from heap: ");
        while (!heap.isEmpty()) {
            System.out.print(heap.poll() + " "); // 1 2 3 4 5 8 9
        }
        System.out.println();

        // Useful methods
        System.out.println("\nSize: " + heap.size());
        System.out.println("isEmpty: " + heap.isEmpty());
    }
}

How it works internally: The heap is stored as an array where for node at index i:

  • Left child: 2*i + 1
  • Right child: 2*i + 2
  • Parent: (i-1) / 2

When you offer(), the element is placed at the end and bubbled up. When you poll(), the root is removed, the last element moves to root, and it bubbles down.

Max-Heap

Max-Heap with Custom Comparator

To create a max-heap (largest element at head), provide a Comparator that reverses the natural ordering.

import java.util.*;

public class MaxHeapDemo {
    public static void main(String[] args) {
        // Method 1: Comparator.reverseOrder()
        PriorityQueue<Integer> maxHeap = new PriorityQueue<>(Comparator.reverseOrder());
        maxHeap.offer(30);
        maxHeap.offer(10);
        maxHeap.offer(50);
        maxHeap.offer(20);
        maxHeap.offer(40);

        System.out.println("Max-heap (peek): " + maxHeap.peek()); // 50
        System.out.println("Polling elements:");
        while (!maxHeap.isEmpty()) {
            System.out.print(maxHeap.poll() + " "); // 50 40 30 20 10
        }
        System.out.println();

        // Method 2: Lambda comparator
        PriorityQueue<Integer> maxHeap2 = new PriorityQueue<>((a, b) -> b - a);
        maxHeap2.offer(30);
        maxHeap2.offer(10);
        maxHeap2.offer(50);
        System.out.println("\nLambda max-heap: " + maxHeap2.peek()); // 50

        // Method 3: Custom object sorting
        PriorityQueue<String> longestFirst = new PriorityQueue<>(
            Comparator.comparingInt(String::length).reversed()
        );
        longestFirst.offer("Hi");
        longestFirst.offer("Hello");
        longestFirst.offer("Hey");
        longestFirst.offer("Greetings");
        System.out.println("\nBy length (longest first):");
        while (!longestFirst.isEmpty()) {
            System.out.println("  " + longestFirst.poll());
        }

        // Custom class with Comparator
        class Task {
            String name;
            int priority;
            Task(String name, int priority) {
                this.name = name;
                this.priority = priority;
            }
            @Override
            public String toString() { return name + "(p" + priority + ")"; }
        }

        PriorityQueue<Task> tasks = new PriorityQueue<>(
            Comparator.comparingInt((Task t) -> t.priority)
        );
        tasks.offer(new Task("Low priority", 3));
        tasks.offer(new Task("High priority", 1));
        tasks.offer(new Task("Medium priority", 2));

        System.out.println("\nBy priority:");
        while (!tasks.isEmpty()) {
            System.out.println("  " + tasks.poll());
        }
    }
}

Common patterns:

  • new PriorityQueue<>(Comparator.reverseOrder()) — max-heap
  • new PriorityQueue<>((a, b) -> b - a) — max-heap (lambda)
  • new PriorityQueue<>(Comparator.comparingInt(...)) — sort by int field
  • .reversed() — reverse any comparator

Methods

Core Methods of PriorityQueue

Method Time Complexity Description
offer(e) O(log n) Add element to heap
poll() O(log n) Remove and return head (smallest)
peek() O(1) Return head without removing
size() O(1) Number of elements
isEmpty() O(1) Check if empty
contains(o) O(n) Search for element
remove(o) O(n) Remove specific element
clear() O(n) Remove all elements
import java.util.*;

public class PriorityQueueMethodsDemo {
    public static void main(String[] args) {
        PriorityQueue<Integer> pq = new PriorityQueue<>();

        // offer - add element
        pq.offer(5);
        pq.offer(1);
        pq.offer(3);
        pq.offer(2);
        pq.offer(4);
        System.out.println("Heap: " + pq); // [1, 2, 3, 5, 4]

        // peek - view smallest without removing
        System.out.println("Peek: " + pq.peek()); // 1
        System.out.println("Size after peek: " + pq.size()); // 5

        // poll - remove smallest
        System.out.println("Poll: " + pq.poll()); // 1
        System.out.println("Poll: " + pq.poll()); // 2
        System.out.println("Size after polls: " + pq.size()); // 3

        // contains - O(n) search
        System.out.println("Contains 3: " + pq.contains(3)); // true
        System.out.println("Contains 1: " + pq.contains(1)); // false (was polled)

        // remove specific element
        pq.remove(3);
        System.out.println("After remove(3): " + pq); // [4, 5]

        // toArray
        Object[] array = pq.toArray();
        System.out.println("Array: " + Arrays.toString(array));

        // iterator - does NOT guarantee order!
        System.out.println("Iterator (no order guarantee):");
        for (Integer num : pq) {
            System.out.print(num + " ");
        }
        System.out.println();

        // drainTo - remove all elements efficiently
        PriorityQueue<Integer> drain = new PriorityQueue<>(Arrays.asList(1, 2, 3, 4, 5));
        List<Integer> drained = new ArrayList<>();
        drain.drainTo(drained);
        System.out.println("Drained: " + drained); // [1, 2, 3, 4, 5]
        System.out.println("Original empty: " + drain.isEmpty()); // true
    }
}

Important notes:

  • poll() returns null if empty (unlike remove() which throws)
  • peek() returns null if empty
  • The iterator does NOT guarantee heap order — use poll() for ordered access
  • drainTo() is more efficient than calling poll() in a loop

Internal Structure

Internal Structure: Balanced Binary Heap

PriorityQueue is backed by a balanced binary heap stored as an array.

Array representation of a heap:

Index:  0  1  2  3  4  5  6
Value:  1  3  2  7  5  8  4

Tree form:
           1
         /   \
        3     2
       / \\   / \
      7   5 8   4

Formulas:

  • Parent of node i: (i - 1) / 2
  • Left child of node i: 2 * i + 1
  • Right child of node i: 2 * i + 2
import java.util.*;

public class HeapStructureDemo {
    public static void main(String[] args) {
        // Visualize heap operations
        PriorityQueue<Integer> heap = new PriorityQueue<>();

        // Insert elements one by one
        int[] elements = {5, 3, 8, 1, 2, 9, 4};
        for (int e : elements) {
            heap.offer(e);
            System.out.println("Added " + e + ": heap = " + heap);
        }

        System.out.println("\n--- Extracting in order ---");
        while (!heap.isEmpty()) {
            System.out.println("Poll " + heap.poll() + ": remaining = " + heap);
        }

        // Demonstrate heap property
        System.out.println("\n--- Heap property ---");
        System.out.println("Min-heap: parent <= children");
        System.out.println("For node at index i:");
        System.out.println("  parent = (i-1)/2");
        System.out.println("  left = 2*i + 1");
        System.out.println("  right = 2*i + 2");
    }
}

How offer() works (bubble up):

  1. Add element at end of array
  2. Compare with parent
  3. If smaller than parent, swap with parent
  4. Repeat until heap property is restored

How poll() works (bubble down):

  1. Save root element (minimum)
  2. Move last element to root
  3. Compare with children
  4. If larger than a child, swap with smaller child
  5. Repeat until heap property is restored

Both operations are O(log n) because the tree has height log(n).

DSA Applications

DSA Applications of PriorityQueue

PriorityQueue is essential for many algorithm patterns:

import java.util.*;

public class PriorityQueueDSA {

    // Pattern 1: Top K Elements
    public static int[] topK(int[] nums, int k) {
        PriorityQueue<Integer> minHeap = new PriorityQueue<>();
        for (int num : nums) {
            minHeap.offer(num);
            if (minHeap.size() > k) {
                minHeap.poll(); // remove smallest
            }
        }
        int[] result = new int[k];
        for (int i = k - 1; i >= 0; i--) {
            result[i] = minHeap.poll();
        }
        return result;
    }

    // Pattern 2: Kth Largest Element
    public static int kthLargest(int[] nums, int k) {
        PriorityQueue<Integer> minHeap = new PriorityQueue<>();
        for (int num : nums) {
            minHeap.offer(num);
            if (minHeap.size() > k) {
                minHeap.poll();
            }
        }
        return minHeap.peek();
    }

    // Pattern 3: Merge K Sorted Lists
    public static List<Integer> mergeKSorted(List<List<Integer>> lists) {
        PriorityQueue<int[]> minHeap = new PriorityQueue<>(
            Comparator.comparingInt(a -> a[1])
        );
        List<Integer> result = new ArrayList<>();

        for (int i = 0; i < lists.size(); i++) {
            if (!lists.get(i).isEmpty()) {
                minHeap.offer(new int[]{i, 0});
            }
        }

        while (!minHeap.isEmpty()) {
            int[] current = minHeap.poll();
            int listIdx = current[0];
            int elemIdx = current[1];
            result.add(lists.get(listIdx).get(elemIdx));
            if (elemIdx + 1 < lists.get(listIdx).size()) {
                minHeap.offer(new int[]{listIdx, elemIdx + 1});
            }
        }

        return result;
    }

    // Pattern 4: Task Scheduler
    public static int leastInterval(char[] tasks, int n) {
        int[] freq = new int[26];
        for (char c : tasks) freq[c - 'A']++;
        PriorityQueue<Integer> maxHeap = new PriorityQueue<>(Comparator.reverseOrder());
        for (int f : freq) {
            if (f > 0) maxHeap.offer(f);
        }
        int time = 0;
        while (!maxHeap.isEmpty()) {
            List<Integer> temp = new ArrayList<>();
            for (int i = 0; i <= n && !maxHeap.isEmpty(); i++) {
                temp.add(maxHeap.poll() - 1);
                time++;
            }
            for (int t : temp) {
                if (t > 0) maxHeap.offer(t);
            }
            if (!maxHeap.isEmpty()) time += n - temp.size() + 1;
        }
        return time;
    }

    public static void main(String[] args) {
        int[] nums = {3, 1, 5, 12, 2, 11};
        System.out.println("Top 3: " + Arrays.toString(topK(nums, 3)));
        System.out.println("Kth largest (k=3): " + kthLargest(nums, 3));

        List<List<Integer>> lists = Arrays.asList(
            Arrays.asList(1, 4, 7),
            Arrays.asList(2, 5, 8),
            Arrays.asList(3, 6, 9)
        );
        System.out.println("Merged: " + mergeKSorted(lists));
    }
}

Key patterns:

  • Top K: Use min-heap of size K
  • Kth largest: Min-heap of size K, peek is the answer
  • Merge K sorted: Min-heap with indices into each list
  • Median finding: Two heaps (max-heap for lower half, min-heap for upper half)

Practice Problems

0/4solved
Kth Largest Element

Find the kth largest element in an unsorted array using a PriorityQueue.

Solution
import java.util.*;

public class KthLargest {
    public static int findKthLargest(int[] nums, int k) {
        PriorityQueue<Integer> minHeap = new PriorityQueue<>();
        for (int num : nums) {
            minHeap.offer(num);
            if (minHeap.size() > k) {
                minHeap.poll();
            }
        }
        return minHeap.peek();
    }
}
Top K Frequent Elements

Given an array of integers, return the k most frequent elements. Use a HashMap for counting and a PriorityQueue for selection.

Solution
import java.util.*;

public class TopKFrequent {
    public static int[] topKFrequent(int[] nums, int k) {
        Map<Integer, Integer> freq = new HashMap<>();
        for (int num : nums) freq.merge(num, 1, Integer::sum);
        PriorityQueue<Map.Entry<Integer, Integer>> minHeap = new PriorityQueue<>(
            Comparator.comparingInt(Map.Entry::getValue)
        );
        for (Map.Entry<Integer, Integer> entry : freq.entrySet()) {
            minHeap.offer(entry);
            if (minHeap.size() > k) minHeap.poll();
        }
        int[] result = new int[k];
        for (int i = 0; i < k; i++) result[i] = minHeap.poll().getKey();
        return result;
    }
}
Merge K Sorted Arrays

Given k sorted arrays, merge them into a single sorted array using a PriorityQueue.

Solution
import java.util.*;

public class MergeKSorted {
    public static int[] merge(int[][] arrays) {
        PriorityQueue<int[]> minHeap = new PriorityQueue<>(Comparator.comparingInt(a -> a[1]));
        List<Integer> result = new ArrayList<>();
        for (int i = 0; i < arrays.length; i++) {
            if (arrays[i].length > 0) minHeap.offer(new int[]{i, 0});
        }
        while (!minHeap.isEmpty()) {
            int[] curr = minHeap.poll();
            int arrIdx = curr[0], elemIdx = curr[1];
            result.add(arrays[arrIdx][elemIdx]);
            if (elemIdx + 1 < arrays[arrIdx].length) {
                minHeap.offer(new int[]{arrIdx, elemIdx + 1});
            }
        }
        return result.stream().mapToInt(i -> i).toArray();
    }
}
Median of Data Stream

Design a data structure that finds the median of a stream of integers. Use two PriorityQueue instances: a max-heap for the lower half and a min-heap for the upper half.

Solution
import java.util.*;

public class MedianFinder {
    private PriorityQueue<Integer> maxHeap; // lower half
    private PriorityQueue<Integer> minHeap; // upper half

    public MedianFinder() {
        maxHeap = new PriorityQueue<>(Comparator.reverseOrder());
        minHeap = new PriorityQueue<>();
    }

    public void addNum(int num) {
        maxHeap.offer(num);
        minHeap.offer(maxHeap.poll());
        if (minHeap.size() > maxHeap.size()) {
            maxHeap.offer(minHeap.poll());
        }
    }

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

Quiz

1. What is the default behavior of PriorityQueue in Java?

Question 1 options

2. What is the time complexity of offer() and poll() in PriorityQueue?

Question 2 options

3. How do you create a max-heap from PriorityQueue?

Question 3 options

4. What internal data structure does PriorityQueue use?

Question 4 options

Flashcards

Question

What is the difference between PriorityQueue and LinkedList for queue operations?

Answer

PriorityQueue orders by priority (min-heap). LinkedList orders by insertion time (FIFO). PriorityQueue: O(log n) add/remove. LinkedList: O(1) add/remove at ends.

Question

How do you find the top K elements using PriorityQueue?

Answer

Use a min-heap of size K. For each element, add to heap and remove the smallest if size exceeds K. The heap contains the K largest elements. Peek gives the Kth largest.

Question

How does PriorityQueue's iterator work?

Answer

The iterator does NOT guarantee heap order. It returns elements in arbitrary order. Use poll() or peek() to access elements in priority order.

Question

What is the formula for finding parent and children in a heap array?

Answer

Parent of i: (i-1)/2. Left child of i: 2*i+1. Right child of i: 2*i+2. This allows array-based storage without pointers.

Question

What is Java PriorityQueue?

Answer

Java PriorityQueue is a key concept in Java programming.

Revision Notes

Key Takeaways

  • 1.PriorityQueue is a min-heap by default (smallest element at head)
  • 2.Use Comparator.reverseOrder() for max-heap
  • 3.offer/poll are O(log n), peek is O(1)
  • 4.Iterator does NOT guarantee heap order — use poll() for ordered access
  • 5.Essential for top K, merge K sorted, median finding patterns

Interview Tips

  • Explain the difference between PriorityQueue and LinkedList for queue operations
  • Demonstrate the top K pattern with a min-heap of size K
  • Explain the two-heap technique for finding the median of a stream
  • Know the internal array representation and formulas for parent/children

Cheat Sheet

PriorityQueue Cheat Sheet

Defaults

  • Min-heap (smallest first)
  • O(log n) offer/poll, O(1) peek
  • No nulls, not synchronized

Creating Max-Heap

new PriorityQueue<>(Comparator.reverseOrder())
new PriorityQueue<>((a, b) -> b - a)

Key Methods

  • offer(e) → add, O(log n)
  • poll() → remove head, O(log n)
  • peek() → view head, O(1)
  • drainTo(collection) → efficient bulk remove

Internal

  • Balanced binary heap (array)
  • Parent: (i-1)/2, Children: 2i+1, 2i+2

Patterns

  • Top K: min-heap of size K
  • Kth largest: min-heap of size K, peek
  • Merge K: min-heap with indices
  • Median: two heaps (max + min)