DSA · Lesson 9 of 17

Heaps & Priority Queues

⏱ 14 min🎬 Interactive visualizer💻 Python · Java · C++ · JavaScript🧩 4 practice problems

A heap always hands you the smallest (or largest) item in O(log n). The tool for top-k, k-way merge, scheduling and Dijkstra.

The idea

A binary heap is a complete binary tree stored in an array where every parent is ≤ its children (a min-heap). The minimum is always at index 0. Children of index i are 2i + 1 and 2i + 2; the parent is (i − 1) / 2.

Patterns

Language notes

Language Min-heap Max-heap
Python heapq (min only) push -x
Java new PriorityQueue<>() new PriorityQueue<>(Collections.reverseOrder())
C++ priority_queue<int, vector<int>, greater<int>> priority_queue<int> (max by default!)
JavaScript none built in: write a small heap class (included in our solutions) same class, reversed comparator

Code template

import heapq

def k_largest(nums, k):
    heap = []
    for x in nums:
        heapq.heappush(heap, x)
        if len(heap) > k:
            heapq.heappop(heap)      # evict the smallest
    return sorted(heap, reverse=True)

# max-heap trick: store negatives
mx = []
heapq.heappush(mx, -5); largest = -mx[0]
List<Integer> kLargest(int[] nums, int k) {
    PriorityQueue<Integer> heap = new PriorityQueue<>();        // min-heap
    for (int x : nums) { heap.offer(x); if (heap.size() > k) heap.poll(); }
    List<Integer> res = new ArrayList<>(heap);
    res.sort(Collections.reverseOrder());
    return res;
}

PriorityQueue<Integer> maxHeap = new PriorityQueue<>(Collections.reverseOrder());
vector<int> kLargest(const vector<int>& nums, int k) {
    priority_queue<int, vector<int>, greater<int>> heap;       // min-heap
    for (int x : nums) { heap.push(x); if ((int)heap.size() > k) heap.pop(); }
    vector<int> res;
    while (!heap.empty()) { res.push_back(heap.top()); heap.pop(); }
    reverse(res.begin(), res.end());
    return res;
}

priority_queue<int> maxHeap;   // max-heap by default
class MinHeap {
  constructor(less = (a, b) => a < b) { this.a = []; this.less = less; }
  get size() { return this.a.length; }
  peek() { return this.a[0]; }
  push(x) {
    const a = this.a; a.push(x); let i = a.length - 1;
    while (i > 0) { const p = (i - 1) >> 1; if (!this.less(a[i], a[p])) break; [a[i], a[p]] = [a[p], a[i]]; i = p; }
  }
  pop() {
    const a = this.a, top = a[0], last = a.pop();
    if (a.length) {
      a[0] = last; let i = 0;
      for (;;) {
        const l = 2 * i + 1, r = l + 1; let m = i;
        if (l < a.length && this.less(a[l], a[m])) m = l;
        if (r < a.length && this.less(a[r], a[m])) m = r;
        if (m === i) break;
        [a[i], a[m]] = [a[m], a[i]]; i = m;
      }
    }
    return top;
  }
}

Practise this pattern (4 problems)

Each problem has a statement, hints, approaches, solutions in 4 languages and a code runner. Solve them in order: easy first.

Common mistakes

Check yourself: to find the 3 largest of a million numbers, min-heap or max-heap? A min-heap of size 3. Its root is the smallest of the current top 3, the one to evict when something bigger arrives.
Created by Ajeetkumar