Skip to content
intermediatePhase 4 · Trees & Heaps

Heap / Priority Queue

Master heap data structure for priority-based operations and scheduling.

1h
5 problems
Topic Progress0%

Heap Fundamentals

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

  • Min-heap: Parent ≤ children (smallest at root)
  • Max-heap: Parent ≥ children (largest at root)

Node Definition (Array-Based)

// No explicit node class needed - use array
// For node at index i:
//   Left child: 2*i + 1
//   Right child: 2*i + 2
//   Parent: (i-1) / 2

Visual Example (Min-Heap)

Array:  [1, 3, 5, 7, 9, 8, 6]

Tree:
          1
        /   \
       3     5
      / \\   / \
     7   9 8   6

Index:  0  1  2  3  4  5  6

Java PriorityQueue

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

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

// Min-heap with custom comparator
PriorityQueue<int[]> minHeap = new PriorityQueue<>((a, b) -> a[0] - b[0]);

// Operations
minHeap.offer(5);     // Insert - O(log n)
int min = minHeap.poll();  // Extract min - O(log n)
int peek = minHeap.peek(); // View min - O(1)
int size = minHeap.size(); // Size - O(1)

Manual Heap Implementation

class MinHeap {
    private int[] heap;
    private int size;
    private int capacity;

    public MinHeap(int capacity) {
        this.capacity = capacity;
        this.size = 0;
        heap = new int[capacity];
    }

    public void insert(int val) {
        if (size == capacity) throw new RuntimeException("Heap full");
        heap[size] = val;
        size++;
        siftUp(size - 1);
    }

    public int extractMin() {
        if (size == 0) throw new RuntimeException("Heap empty");
        int min = heap[0];
        heap[0] = heap[size - 1];
        size--;
        siftDown(0);
        return min;
    }

    private void siftUp(int i) {
        while (i > 0) {
            int parent = (i - 1) / 2;
            if (heap[i] < heap[parent]) {
                swap(i, parent);
                i = parent;
            } else break;
        }
    }

    private void siftDown(int i) {
        while (true) {
            int smallest = i;
            int left = 2 * i + 1;
            int right = 2 * i + 2;
            if (left < size && heap[left] < heap[smallest]) smallest = left;
            if (right < size && heap[right] < heap[smallest]) smallest = right;
            if (smallest != i) {
                swap(i, smallest);
                i = smallest;
            } else break;
        }
    }

    private void swap(int i, int j) {
        int temp = heap[i];
        heap[i] = heap[j];
        heap[j] = temp;
    }
}

Heapify (Build Heap from Array) - O(n)

public 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 void siftDown(int[] arr, int n, int i) {
    int largest = i;
    int left = 2 * i + 1;
    int right = 2 * i + 2;
    if (left < n && arr[left] > arr[largest]) largest = left;
    if (right < n && arr[right] > arr[largest]) largest = right;
    if (largest != i) {
        swap(arr, i, largest);
        siftDown(arr, n, largest);
    }
}

Complexity

Operation Time
Insert O(log n)
Extract min/max O(log n)
Peek O(1)
Heapify O(n)
Search O(n)

Space: O(n)

Heap Applications

Top K Elements

Find K largest elements using min-heap of size K:

public 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 = 0; i < k; i++) {
        result[i] = minHeap.poll();
    }
    return result;
}

Kth Largest Element

public 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();
}

Median Finder (Two Heaps)

class MedianFinder {
    PriorityQueue<Integer> maxHeap;  // Lower half
    PriorityQueue<Integer> minHeap;  // Upper half

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

    public void addNum(int num) {
        maxHeap.offer(num);
        minHeap.offer(maxHeap.poll());  // Balance
        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;
    }
}

Merge K Sorted Lists

public ListNode mergeKLists(ListNode[] lists) {
    PriorityQueue<ListNode> pq = new PriorityQueue<>((a, b) -> a.val - b.val);
    for (ListNode list : lists) {
        if (list != null) pq.offer(list);
    }
    ListNode dummy = new ListNode(0);
    ListNode curr = dummy;
    while (!pq.isEmpty()) {
        curr.next = pq.poll();
        curr = curr.next;
        if (curr.next != null) pq.offer(curr.next);
    }
    return dummy.next;
}

Task Scheduler

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

    PriorityQueue<Integer> maxHeap = new PriorityQueue<>(Collections.reverseOrder());
    for (int c : count) {
        if (c > 0) maxHeap.offer(c);
    }

    int intervals = 0;
    while (!maxHeap.isEmpty()) {
        int cycle = 0;
        List<Integer> temp = new ArrayList<>();
        for (int i = 0; i <= n; i++) {
            if (!maxHeap.isEmpty()) {
                int val = maxHeap.poll() - 1;
                if (val > 0) temp.add(val);
                cycle++;
            }
        }
        for (int val : temp) maxHeap.offer(val);
        intervals += maxHeap.isEmpty() ? cycle : n + 1;
    }
    return intervals;
}

When to Use Heap

  • Top K / Kth element: min-heap of size K
  • Merge sorted: K-way merge with min-heap
  • Median maintenance: two heaps (max + min)
  • Scheduling/priorities: process by priority
  • Sliding window max/min: heap with lazy deletion

Practice Problems

0/3solved
Kth Largest Element in a Stream
Min-Heap of Size K

Design a class to find the kth largest element in a stream.

Example:

Input: KthLargest(3, [4,5,8,2]).add(3) → 4

Output: 4

3rd largest in [2,3,4,5,8] is 4.

Optimal Solution — O(log k) per add time, O(k) space

Min-heap of size K

class KthLargest {
    PriorityQueue<Integer> minHeap;
    int k;
    public KthLargest(int k, int[] nums) {
        this.k = k;
        minHeap = new PriorityQueue<>();
        for (int num : nums) add(num);
    }
    public int add(int val) {
        minHeap.offer(val);
        if (minHeap.size() > k) minHeap.poll();
        return minHeap.peek();
    }
}

Edge Cases:

  • k equals array length
  • Adding smallest element
  • Adding largest element
Find Median from Data Stream
Two Heaps

Design a data structure that supports addNum and findMedian. Median is the middle value.

Example:

Input: addNum(1), addNum(2), findMedian() → 1.5, addNum(3), findMedian() → 2

Output: [null, null, 1.5, null, 2]

Median of [1,2] is 1.5, median of [1,2,3] is 2.

Optimal Solution — O(log n) add, O(1) findMedian time, O(n) space

Two heaps: max-heap for lower half, min-heap for upper half

class MedianFinder {
    PriorityQueue<Integer> maxHeap;
    PriorityQueue<Integer> minHeap;
    
    public MedianFinder() {
        maxHeap = new PriorityQueue<>(Collections.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;
    }
}

Edge Cases:

  • Single element
  • Even number of elements
  • All same values
Task Scheduler
Greedy + Heap

Given a characters array tasks where each task must be done at least once. Each task can be done in one unit of time. There is a non-negative integer n that represents the cooldown period between two same tasks. Return the least number of intervals the CPU will take to finish all tasks.

Example:

Input: tasks = ['A','A','A','B','B','B'], n = 2

Output: 8

A -> B -> idle -> A -> B -> idle -> A -> B

Optimal Solution — O(n) time, O(1) space

Greedy: schedule most frequent tasks first, use cooldown

class Solution {
    public int leastInterval(char[] tasks, int n) {
        int[] count = new int[26];
        for (char c : tasks) count[c - 'A']++;
        Arrays.sort(count);
        int maxFreq = count[25];
        int idleSlots = (maxFreq - 1) * n;
        for (int i = 24; i >= 0; i--) {
            idleSlots -= Math.min(count[i], maxFreq - 1);
        }
        return Math.max(tasks.length, tasks.length + Math.max(0, idleSlots));
    }
}

Edge Cases:

  • n = 0: return tasks.length
  • Single task type
  • All unique tasks

Quiz

1. What is the time complexity of building a heap from an unsorted array?

Question 1 options

2. To find the Kth largest element, which heap should you use?

Question 2 options

3. What is the primary purpose of Heap (Priority Queue)?

Question 3 options

4. What is a common mistake when implementing Heap (Priority Queue)?

Question 4 options

Flashcards

Question

What is the difference between min-heap and max-heap?

Answer

Min-heap: parent ≤ children, root is minimum. Max-heap: parent ≥ children, root is maximum. Use min-heap for Kth largest (keep K largest), max-heap for Kth smallest.

Question

What is the time complexity of heapify (build heap from array)?

Answer

O(n) - not O(n log n). Most nodes are leaves and require no sifting. The sum of sift-down heights converges to O(n).

Question

What is Heap (Priority Queue)?

Answer

Heap (Priority Queue) is a key concept in software engineering.

Question

When to use Heap (Priority Queue)?

Answer

Use Heap (Priority Queue) when building production systems that require reliability, scalability, and maintainability.

Question

Heap (Priority Queue) best practices

Answer

Follow SOLID principles, write clean code, test thoroughly, document decisions, and monitor in production.

Revision Notes

Key Takeaways

  • 1.Heap is a complete binary tree stored as an array
  • 2.Min-heap for Kth largest, max-heap for Kth smallest
  • 3.Heapify is O(n), not O(n log n)
  • 4.Two heaps (max + min) solve median maintenance problems

Interview Tips

  • Know Java PriorityQueue API: offer, poll, peek, size
  • For top-K, use heap of size K (not sorting all elements)
  • Mention that heap is not sorted - only root is guaranteed
  • For median, explain the two-heap invariant clearly

Cheat Sheet

Heap Cheat Sheet

Heap Property:

  • Min-heap: parent ≤ children (root = min)
  • Max-heap: parent ≥ children (root = max)

Array Navigation:

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

Java PriorityQueue:

PriorityQueue<Integer> minHeap = new PriorityQueue<>();
PriorityQueue<Integer> maxHeap = new PriorityQueue<>(Collections.reverseOrder());

minHeap.offer(val);  // Insert O(log n)
int min = minHeap.poll();  // Extract min O(log n)
int peek = minHeap.peek();  // View min O(1)

Operations:

Operation Time
Insert O(log n)
Extract O(log n)
Peek O(1)
Heapify O(n)

Common Patterns:

  • Kth largest → min-heap of size K
  • Kth smallest → max-heap of size K
  • Median → two heaps (max + min)
  • Merge K sorted → min-heap with K lists
  • Sliding window max → heap with lazy deletion

Key Insight: Heap gives O(log n) insert + O(1) peek + O(log n) extract. Use when you need repeated min/max access.