Heap & Priority Queue: Code Templates in 6 Languages
If you have not read the concept guide yet, start there for the intuition and complexity analysis. This page gives you the code to memorize and adapt during an interview. Every template below runs a heap of size k or smaller, so the time is
The Heap Helpers for JavaScript and Ruby
Python, Java, C++, and Go ship a heap in the standard library, so those tabs use the built-ins directly. JavaScript and Ruby do not, so every JavaScript and Ruby tab on this page uses one of the helper classes below. The helper takes a less function that decides which of two values outranks the other. The default is a min-heap, where smaller values rank higher.
class Heap {
constructor(values = [], less = (a, b) => a < b) {
this.less = less;
this.items = values.slice();
// batch construction sinks every non-leaf once.
// doing it this way costs O(N) instead of O(N log N)
for (let i = Math.floor(this.items.length / 2) - 1; i >= 0; i--) {
this._sink(i);
}
}
size() {
return this.items.length;
}
peek() {
return this.items[0];
}
push(value) {
this.items.push(value);
this._rise(this.items.length - 1);
}
pop() {
if (this.items.length === 1) return this.items.pop();
const top = this.items[0];
// move the last element to the root, then sink the
// new root back into a valid position
this.items[0] = this.items.pop();
this._sink(0);
return top;
}
// a freshly appended element walks toward the root while
// it outranks its parent
_rise(i) {
while (i > 0) {
const parent = (i - 1) >> 1;
if (!this.less(this.items[i], this.items[parent])) break;
[this.items[i], this.items[parent]] = [this.items[parent], this.items[i]];
i = parent;
}
}
// the root replacement walks down while a child outranks it
_sink(i) {
const n = this.items.length;
while (true) {
let best = i;
const left = 2 * i + 1;
const right = 2 * i + 2;
if (left < n && this.less(this.items[left], this.items[best])) best = left;
if (right < n && this.less(this.items[right], this.items[best])) best = right;
if (best === i) break;
[this.items[i], this.items[best]] = [this.items[best], this.items[i]];
i = best;
}
}
}class Heap
def initialize(values = [], &less)
@less = less || ->(a, b) { a < b }
@items = values.dup
# batch construction sinks every non-leaf once.
# doing it this way costs O(N) instead of O(N log N)
(@items.length / 2 - 1).downto(0) { |i| sink(i) }
end
def size
@items.length
end
def peek
@items[0]
end
def push(value)
@items << value
rise(@items.length - 1)
end
def pop
return @items.pop if @items.length == 1
top = @items[0]
# move the last element to the root, then sink the
# new root back into a valid position
@items[0] = @items.pop
sink(0)
top
end
private
# a freshly appended element walks toward the root while
# it outranks its parent
def rise(i)
while i > 0
parent = (i - 1) / 2
break unless @less.call(@items[i], @items[parent])
@items[i], @items[parent] = @items[parent], @items[i]
i = parent
end
end
# the root replacement walks down while a child outranks it
def sink(i)
n = @items.length
loop do
best = i
left = 2 * i + 1
right = 2 * i + 2
best = left if left < n && @less.call(@items[left], @items[best])
best = right if right < n && @less.call(@items[right], @items[best])
break if best == i
@items[i], @items[best] = @items[best], @items[i]
i = best
end
end
endMain Template: Top-K with a Size-k Min-Heap
The most common heap problem is finding the k largest or k smallest elements. The trick is a min-heap capped at size k. Push every element, and pop whenever the heap exceeds k. The root is then the kth largest of everything seen so far, because every element that got popped was the smallest of a set of k+1 elements that all arrived before the final answer could be known.
Use this for Kth Largest Element in a Stream and Kth Largest Element in an Array .
function topK(nums, k) {
// a min-heap of size k keeps the k largest values.
// the root is the smallest of those k, so it is the
// element that must leave when a larger one arrives
const heap = new Heap();
for (const num of nums) {
heap.push(num);
if (heap.size() > k) {
heap.pop();
}
}
const result = [];
while (heap.size() > 0) {
result.push(heap.pop());
}
return result;
}Code Breakdown
Key Variables
heap(orh): the priority queue, always kept at size k or smaller.k: how many extreme elements to keep. The cap is the whole point of the template.root/top: the smallest of the k largest values. It is the value that leaves when a bigger value arrives, which is why a min-heap is the right shape for top-K.
Visual Mechanism
graph TD
A["Next input value"] --> B["Push into the min-heap"]
B --> C{"Heap size > k?"}
C -->|Yes| D["Pop the root"]
C -->|No| E["Keep the value"]
D --> E
E --> F["Root is the kth largest so far"]
Critical Sections
The push is a normal heap insert at
The cap check is the section that makes the template correct. After every push, if the heap holds more than k elements, pop. The popped value is the smallest of the current k+1, so it cannot be part of the final answer. Skipping this check turns the heap into a full min-heap whose root is the global minimum, which is a completely different answer.
The peek is a free read. The root always holds the kth largest, so the answer is available at any moment, not just at the end. That property is what makes the template work for streaming input.
Variations
1. Max-Heap for k Smallest
The mirror image of the main template. To keep the k smallest values, use a max-heap capped at k. The root is the largest of the k smallest, so it is the one that leaves when a smaller value arrives.
import heapq
def smallest_k(nums, k):
# heapq is a min-heap, so negate every value. the root
# is then the largest of the k smallest values and the
# one that must leave when a smaller value arrives.
heap = []
for num in nums:
heapq.heappush(heap, -num)
if len(heap) > k:
heapq.heappop(heap)
return [-x for x in heap]The JavaScript and Ruby helpers from the top of this page make the same flip with a one-line comparator: new Heap([], (a, b) => a > b) in JavaScript and Heap.new { |a, b| a > b } in Ruby.
2. Two Heaps for a Running Median
Keep the lower half of the data in a max-heap and the upper half in a min-heap. Each insert pushes into the lower half, moves the largest lower value into the upper half, then rebalances so the lower half holds the extra element when the count is odd. The median is then a root read.
Use this for Find Median from Data Stream .
class MedianFinder {
constructor() {
// low is a max-heap, high is a min-heap
this.low = new Heap([], (a, b) => a > b);
this.high = new Heap();
}
addNum(num) {
// push into low, then move low's largest into high.
// this keeps every low value below every high value
this.low.push(num);
this.high.push(this.low.pop());
// low holds the extra element when the count is odd
if (this.low.size() < this.high.size()) {
this.low.push(this.high.pop());
}
}
findMedian() {
if (this.low.size() > this.high.size()) {
return this.low.peek();
}
return (this.low.peek() + this.high.peek()) / 2;
}
}The rebalance step runs after every insert, not just sometimes. Skipping it lets the two halves drift, and the median read starts picking values from the wrong side of the split.
3. K-Way Merge with a Min-Heap
When k sorted lists need to merge into one, push the head of every list into a min-heap. Pop the smallest head, append it to the result, and push the next node from that same list. The heap never holds more than k nodes.
Use this for Merge k Sorted Lists .
function mergeKLists(lists) {
// min-heap of list nodes ordered by value. each list
// contributes its current head; popping a head reveals
// the next node of that same list.
const heap = new Heap([], (a, b) => a.val < b.val);
for (const head of lists) {
if (head) heap.push(head);
}
const dummy = { val: 0, next: null };
let current = dummy;
while (heap.size() > 0) {
current.next = heap.pop();
current = current.next;
if (current.next) {
heap.push(current.next);
}
}
return dummy.next;
}The heap size stays at k, so each push and pop costs
4. Heapify an Existing Array
Building a heap from an existing array costs
import heapq
heap = nums
heapq.heapify(heap)Java and C++ do not expose a batch heapify on their standard heaps, so they build by pushing, which is
Now head to the practice problems to apply these templates to real interview questions.