Skip to content
intermediate Phase 4 · Trees & Heaps

Heap / Priority Queue

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

1h
5 problems
Topic Progress 0%

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 / 4 solved
Kth Largest Element in an Array
Heap

Given an integer array nums and an integer k, return the kth largest element in the array. Note: it is the kth largest element in sorted order, not the kth distinct element.

Example:

Input: nums = [3,2,1,5,6,4], k = 2

Output: 5

The 2nd largest element is 5 (sorted: [1,2,3,4,5,6]).

Edge Cases:

  • k equals array length — answer is the minimum element
  • k equals 1 — answer is the maximum element
  • All elements same value
  • Negative numbers present
  • Single element array
Find Median from Data Stream
Two Heaps

Design a data structure that supports the following two operations: addNum(int num) adds the integer num from the data stream, and findMedian() returns the median of all elements so far.

Example:

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

Output: [null, null, 1.5, null, 2.0]

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

Edge Cases:

  • Single element: median is that element
  • Two elements: median is average
  • Odd number of elements: median is the middle element
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

Edge Cases:

  • n = 0 — just execute all tasks in any order, answer is tasks.length
  • All tasks same type — must insert n idle slots between each
  • More unique task types than n — no idle slots needed
  • Single task type, n large
  • All unique tasks
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.

Edge Cases:

  • k equals nums.length: heap contains all elements
  • k = 1: heap tracks the maximum
  • Empty initial stream: heap builds as adds come in

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 a common mistake when implementing Heap (Priority Queue)?

Question 3 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

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.