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-heapnew 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 (unlikeremove()which throws)peek()returns null if empty- The iterator does NOT guarantee heap order — use
poll()for ordered access drainTo()is more efficient than callingpoll()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):
- Add element at end of array
- Compare with parent
- If smaller than parent, swap with parent
- Repeat until heap property is restored
How poll() works (bubble down):
- Save root element (minimum)
- Move last element to root
- Compare with children
- If larger than a child, swap with smaller child
- 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
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();
}
}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;
}
}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();
}
}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?
2. What is the time complexity of offer() and poll() in PriorityQueue?
3. How do you create a max-heap from PriorityQueue?
4. What internal data structure does PriorityQueue use?
Flashcards
Question
What is the difference between PriorityQueue and LinkedList for queue operations?
Click to reveal answer
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?
Click to reveal answer
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?
Click to reveal answer
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?
Click to reveal answer
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?
Click to reveal answer
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)