Overview
A heap is a complete binary tree stored in an array that keeps the smallest (min-heap) or largest (max-heap) element at the root. Children of index i live at 2i + 1 and 2i + 2, and the parent at floor((i - 1) / 2). Push and pop are O(log n); peeking at the root is O(1).
A priority queue is the abstract idea (always remove the highest-priority item) and a heap is how it is implemented. Heaps power Dijkstra's shortest path, Prim's MST, top K problems, running medians, merging sorted streams, and task schedulers.
Patients are not treated in arrival order but by severity. When a new patient arrives, they are placed according to urgency, and the most critical patient is always seen next. The triage nurse does not fully sort everyone, only keeps the most urgent at the front.
When to use it
- Repeatedly get the smallest or largest item while items are being added.
- Top K, kth largest, or k closest.
- Merging k sorted lists or streams.
- Scheduling by priority or earliest deadline.
- Greedy algorithms that always pick the cheapest next option (Dijkstra, Prim, Huffman).
Problem patterns it solves
Recognize it when: k largest, k smallest, kth largest in an array or stream.
- 215. Kth Largest Element in an Array
- 703. Kth Largest Element in a Stream
- 973. K Closest Points to Origin
Recognize it when: running median, balance lower and upper halves.
- 295. Find Median from Data Stream
- 480. Sliding Window Median
- 502. IPO
Recognize it when: merge several sorted sources.
- 23. Merge k Sorted Lists
- 378. Kth Smallest Element in a Sorted Matrix
- 373. Find K Pairs with Smallest Sums
Recognize it when: pick the most frequent / cheapest / earliest next.
- 621. Task Scheduler
- 1046. Last Stone Weight
- 1834. Single-Threaded CPU
- 767. Reorganize String
Recognize it when: weighted graphs where you expand the cheapest frontier node.
- 743. Network Delay Time
- 1584. Min Cost to Connect All Points
- 787. Cheapest Flights Within K Stops
Where it is used in real software
Priority-based schedulers pick the highest-priority runnable process; timers are kept in heaps ordered by expiry time.
OSPF routers run Dijkstra's algorithm with a priority queue to compute shortest paths.
Delayed-job systems like Sidekiq scheduled sets and Java's DelayQueue release the job with the earliest run time first.
Huffman coding in ZIP and JPEG builds its tree by repeatedly merging the two least frequent symbols from a min-heap.
Key terms
- Heap property
- In a min-heap, every parent <= its children (root is the minimum).
- Sift up (bubble up)
- After push, swap the new element with its parent while it is smaller.
- Sift down (heapify down)
- After pop, move the last element to the root and swap it down with the smaller child.
- Heapify
- Build a heap from an array in O(n) by sifting down from the last parent to the root.
- Complete binary tree
- All levels full except possibly the last, which fills left to right; no gaps in the array.
Push and pop in a min-heap
- 1Push: append at the end
Place the new value at index n to keep the tree complete.
- 2Push: sift up
While the value is smaller than its parent at (i - 1) >> 1, swap them.
- 3Pop: take the root
The minimum is at index 0.
- 4Pop: move the last element to the root
This keeps the tree complete.
- 5Pop: sift down
Swap with the smaller child until both children are larger or it becomes a leaf.
STEP 1Valid min-heap. Children of i are at 2i + 1 and 2i + 2: children of 2 are 4 and 3.
Pop from the min-heap [1, 4, 2, 7, 5, 3]
Remove the minimum and restore the heap
| Step | Array | Action |
|---|---|---|
| 1 | [1, 4, 2, 7, 5, 3] | take root 1 |
| 2 | [3, 4, 2, 7, 5] | move last element (3) to the root |
| 3 | [2, 4, 3, 7, 5] | children of 3 are 4 and 2; smaller is 2, swap |
| 4 | [2, 4, 3, 7, 5] | 3 at index 2 has no children: done |
NOWStep: 1 | Array: [1, 4, 2, 7, 5, 3] | Action: take root 1
Returned 1, and the new minimum 2 is at the root. The work is one path from root to leaf: O(log n).
Implementation
class PriorityQueue { constructor(compare = (a, b) => a - b) { this.heap = []; this.compare = compare; // negative means a has higher priority } get size() { return this.heap.length; } peek() { return this.heap[0]; } push(value) { const h = this.heap; h.push(value); let i = h.length - 1; while (i > 0) { const parent = (i - 1) >> 1; if (this.compare(h[i], h[parent]) >= 0) break; [h[i], h[parent]] = [h[parent], h[i]]; i = parent; } } pop() { const h = this.heap; if (h.length === 0) return undefined; const top = h[0]; const last = h.pop(); if (h.length > 0) { h[0] = last; let i = 0; for (;;) { const left = 2 * i + 1, right = left + 1; let best = i; if (left < h.length && this.compare(h[left], h[best]) < 0) best = left; if (right < h.length && this.compare(h[right], h[best]) < 0) best = right; if (best === i) break; [h[i], h[best]] = [h[best], h[i]]; i = best; } } return top; }} // 295. Find Median from Data Stream with two heapsclass MedianFinder { constructor() { this.low = new PriorityQueue((a, b) => b - a); // max-heap: lower half this.high = new PriorityQueue(); // min-heap: upper half } addNum(num) { this.low.push(num); this.high.push(this.low.pop()); // keep every low <= every high if (this.high.size > this.low.size) this.low.push(this.high.pop()); } findMedian() { return this.low.size > this.high.size ? this.low.peek() : (this.low.peek() + this.high.peek()) / 2; }}Complexity and performance
Root of the array.
One root-to-leaf path.
Most nodes are near the bottom and move little.
Heaps are not sorted.
In place, not stable.
Trade-offs
A sorted array gives O(1) min but O(n) insert. A heap balances both at O(log n).
A BST also gives min and max in O(log n) plus ordered iteration and arbitrary deletion, but with more memory and slower constants.
Heaps cannot delete arbitrary items efficiently. Mark items as removed and skip them when they reach the top.
Variants and related techniques
Tracks each item's position so its priority can be decreased in O(log n); used in optimized Dijkstra.
Each node has d children; shallower tree, faster pushes.
O(1) amortized decrease-key; theoretical speedup for Dijkstra, rarely used in practice.
Common mistakes
- Assuming Java's PriorityQueue is a max-heap.
Fix: It is a min-heap by default; pass Collections.reverseOrder() for max.
- Iterating a PriorityQueue expecting sorted order.
Fix: Iteration order is the array order, not sorted. Poll repeatedly to get sorted output.
- Using sort after each insert in JavaScript.
Fix: That is O(n log n) per insert. Implement or import a binary heap.
- Changing an element's priority in place.
Fix: The heap will not re-order. Remove and re-insert, or push a new entry and lazily skip stale ones.
Interview questions
Why is building a heap O(n) rather than O(n log n)?
Sifting down from the bottom up, about half the nodes are leaves (no work), a quarter move one level, and so on. The sum n/4 x 1 + n/8 x 2 + ... converges to O(n).
How do you find a running median?
Keep a max-heap for the lower half and a min-heap for the upper half, balanced so their sizes differ by at most one. The median is the top of the larger heap, or the average of both tops.
Practice problems
| Problem | Difficulty | What it trains |
|---|---|---|
| 1046. Last Stone Weight | Easy | Max-heap simulation. |
| 703. Kth Largest Element in a Stream | Easy | Min-heap of size k. |
| 215. Kth Largest Element in an Array | Medium | Heap vs quickselect. |
| 621. Task Scheduler | Medium | Greedy with cooldown. |
| 1834. Single-Threaded CPU | Medium | Two orderings. |
| 295. Find Median from Data Stream | Hard | Two heaps. |
| 23. Merge k Sorted Lists | Hard | K-way merge. |