Code Templates
This page provides standardized, memorizable code “blueprints” that can be adapted to most heap and priority queue problems. Each template includes implementations in multiple programming languages to help you master this essential pattern.
Before diving into these templates, make sure you understand the Concept Guide which explains when and why to use heap and priority queue patterns. These templates provide the practical implementation details you’ll need for coding interviews.
The “Main” Template: Binary Heap Implementation
This template provides the most common variation - a binary heap with array-based storage. It’s the foundation for most heap-based solutions.
Description: Array-based binary heap implementation that maintains the heap property through bubble up and sink down operations. This template directly solves problems like Kth Largest Element and Last Stone Weight.
class MaxHeap {
constructor() {
this.heap = [];
}
insert(val) {
this.heap.push(val);
this._bubbleUp(this.heap.length - 1);
}
extractMax() {
if (this.heap.length === 1) return this.heap.pop();
if (this.heap.length === 0) return null;
const max = this.heap[0];
this.heap[0] = this.heap.pop();
this._sinkDown(0);
return max;
}
peek() {
return this.heap.length > 0 ? this.heap[0] : null;
}
size() {
return this.heap.length;
}
_bubbleUp(idx) {
const parentIdx = Math.floor((idx - 1) / 2);
if (parentIdx >= 0 && this.heap[idx] > this.heap[parentIdx]) {
[this.heap[idx], this.heap[parentIdx]] = [this.heap[parentIdx], this.heap[idx]];
this._bubbleUp(parentIdx);
}
}
_sinkDown(idx) {
const leftIdx = 2 * idx + 1;
const rightIdx = 2 * idx + 2;
let largestIdx = idx;
if (leftIdx < this.heap.length && this.heap[leftIdx] > this.heap[largestIdx]) {
largestIdx = leftIdx;
}
if (rightIdx < this.heap.length && this.heap[rightIdx] > this.heap[largestIdx]) {
largestIdx = rightIdx;
}
if (largestIdx !== idx) {
[this.heap[idx], this.heap[largestIdx]] = [this.heap[largestIdx], this.heap[idx]];
this._sinkDown(largestIdx);
}
}
}class MaxHeap:
def __init__(self):
self.heap = []
def insert(self, val):
self.heap.append(val)
self._bubble_up(len(self.heap) - 1)
def extract_max(self):
if len(self.heap) == 1:
return self.heap.pop()
if len(self.heap) == 0:
return None
max_val = self.heap[0]
self.heap[0] = self.heap.pop()
self._sink_down(0)
return max_val
def peek(self):
return self.heap[0] if self.heap else None
def size(self):
return len(self.heap)
def _bubble_up(self, idx):
parent_idx = (idx - 1) // 2
if parent_idx >= 0 and self.heap[idx] > self.heap[parent_idx]:
self.heap[idx], self.heap[parent_idx] = self.heap[parent_idx], self.heap[idx]
self._bubble_up(parent_idx)
def _sink_down(self, idx):
left_idx = 2 * idx + 1
right_idx = 2 * idx + 2
largest_idx = idx
if left_idx < len(self.heap) and self.heap[left_idx] > self.heap[largest_idx]:
largest_idx = left_idx
if right_idx < len(self.heap) and self.heap[right_idx] > self.heap[largest_idx]:
largest_idx = right_idx
if largest_idx != idx:
self.heap[idx], self.heap[largest_idx] = self.heap[largest_idx], self.heap[idx]
self._sink_down(largest_idx)public class MaxHeap {
private int[] heap;
private int size;
public MaxHeap() {
this.heap = new int[10];
this.size = 0;
}
public void insert(int val) {
if (size == heap.length) {
resize();
}
heap[size] = val;
bubbleUp(size);
size++;
}
public int extractMax() {
if (size == 0) return Integer.MIN_VALUE;
int max = heap[0];
heap[0] = heap[size - 1];
size--;
sinkDown(0);
return max;
}
public int peek() {
return size > 0 ? heap[0] : Integer.MIN_VALUE;
}
public int size() {
return size;
}
private void bubbleUp(int idx) {
int parentIdx = (idx - 1) / 2;
if (parentIdx >= 0 && heap[idx] > heap[parentIdx]) {
swap(idx, parentIdx);
bubbleUp(parentIdx);
}
}
private void sinkDown(int idx) {
int leftIdx = 2 * idx + 1;
int rightIdx = 2 * idx + 2;
int largestIdx = idx;
if (leftIdx < size && heap[leftIdx] > heap[largestIdx]) {
largestIdx = leftIdx;
}
if (rightIdx < size && heap[rightIdx] > heap[largestIdx]) {
largestIdx = rightIdx;
}
if (largestIdx != idx) {
swap(idx, largestIdx);
sinkDown(largestIdx);
}
}
private void swap(int i, int j) {
int temp = heap[i];
heap[i] = heap[j];
heap[j] = temp;
}
private void resize() {
int[] newHeap = new int[heap.length * 2];
System.arraycopy(heap, 0, newHeap, 0, heap.length);
heap = newHeap;
}
}#include <vector>
#include <algorithm>
class MaxHeap {
private:
std::vector<int> heap;
public:
void insert(int val) {
heap.push_back(val);
bubbleUp(heap.size() - 1);
}
int extractMax() {
if (heap.size() == 1) {
int val = heap.back();
heap.pop_back();
return val;
}
if (heap.empty()) {
return INT_MIN;
}
int maxVal = heap[0];
heap[0] = heap.back();
heap.pop_back();
sinkDown(0);
return maxVal;
}
int peek() {
return heap.empty() ? INT_MIN : heap[0];
}
int size() {
return heap.size();
}
private:
void bubbleUp(int idx) {
int parentIdx = (idx - 1) / 2;
if (parentIdx >= 0 && heap[idx] > heap[parentIdx]) {
std::swap(heap[idx], heap[parentIdx]);
bubbleUp(parentIdx);
}
}
void sinkDown(int idx) {
int leftIdx = 2 * idx + 1;
int rightIdx = 2 * idx + 2;
int largestIdx = idx;
if (leftIdx < heap.size() && heap[leftIdx] > heap[largestIdx]) {
largestIdx = leftIdx;
}
if (rightIdx < heap.size() && heap[rightIdx] > heap[largestIdx]) {
largestIdx = rightIdx;
}
if (largestIdx != idx) {
std::swap(heap[idx], heap[largestIdx]);
sinkDown(largestIdx);
}
}
};package main
import "math"
type MaxHeap struct {
heap []int
}
func NewMaxHeap() *MaxHeap {
return &MaxHeap{heap: make([]int, 0)}
}
func (h *MaxHeap) Insert(val int) {
h.heap = append(h.heap, val)
h.bubbleUp(len(h.heap) - 1)
}
func (h *MaxHeap) ExtractMax() int {
if len(h.heap) == 1 {
val := h.heap[0]
h.heap = h.heap[:0]
return val
}
if len(h.heap) == 0 {
return math.MinInt
}
max := h.heap[0]
h.heap[0] = h.heap[len(h.heap)-1]
h.heap = h.heap[:len(h.heap)-1]
h.sinkDown(0)
return max
}
func (h *MaxHeap) Peek() int {
if len(h.heap) == 0 {
return math.MinInt
}
return h.heap[0]
}
func (h *MaxHeap) Size() int {
return len(h.heap)
}
func (h *MaxHeap) bubbleUp(idx int) {
parentIdx := (idx - 1) / 2
if parentIdx >= 0 && h.heap[idx] > h.heap[parentIdx] {
h.heap[idx], h.heap[parentIdx] = h.heap[parentIdx], h.heap[idx]
h.bubbleUp(parentIdx)
}
}
func (h *MaxHeap) sinkDown(idx int) {
leftIdx := 2*idx + 1
rightIdx := 2*idx + 2
largestIdx := idx
if leftIdx < len(h.heap) && h.heap[leftIdx] > h.heap[largestIdx] {
largestIdx = leftIdx
}
if rightIdx < len(h.heap) && h.heap[rightIdx] > h.heap[largestIdx] {
largestIdx = rightIdx
}
if largestIdx != idx {
h.heap[idx], h.heap[largestIdx] = h.heap[largestIdx], h.heap[idx]
h.sinkDown(largestIdx)
}
}class MaxHeap
def initialize
@heap = []
end
def insert(val)
@heap << val
bubble_up(@heap.length - 1)
end
def extract_max
return @heap.pop if @heap.length == 1
return nil if @heap.empty?
max = @heap[0]
@heap[0] = @heap.pop
sink_down(0)
max
end
def peek
@heap[0] if @heap.any?
end
def size
@heap.length
end
private
def bubble_up(idx)
parent_idx = (idx - 1) / 2
if parent_idx >= 0 && @heap[idx] > @heap[parent_idx]
@heap[idx], @heap[parent_idx] = @heap[parent_idx], @heap[idx]
bubble_up(parent_idx)
end
end
def sink_down(idx)
left_idx = 2 * idx + 1
right_idx = 2 * idx + 2
largest_idx = idx
if left_idx < @heap.length && @heap[left_idx] > @heap[largest_idx]
largest_idx = left_idx
end
if right_idx < @heap.length && @heap[right_idx] > @heap[largest_idx]
largest_idx = right_idx
end
if largest_idx != idx
@heap[idx], @heap[largest_idx] = @heap[largest_idx], @heap[idx]
sink_down(largest_idx)
end
end
endCode Breakdown
Key Variables and Their Roles
heap: Array storing the complete binary tree representationsize: Current number of elements in the heapidx: Current position being processed in bubble up/sink down operationsparentIdx: Index of parent node:(idx - 1) / 2leftIdx: Index of left child:2 * idx + 1rightIdx: Index of right child:2 * idx + 2
Critical Sections
- Initialization: Create empty array to store heap elements
- Expansion (Insert): Add element to end, then bubble up to maintain heap property
- Contraction (Extract): Replace root with last element, then sink down to restore heap property
Algorithm Flow
graph TD
A[Insert Operation] --> B[Add to end of array]
B --> C[Bubble up if needed]
C --> D[Maintain heap property]
E[Extract Operation] --> F[Remove root]
F --> G[Move last element to root]
G --> H[Sink down if needed]
H --> I[Maintain heap property]
Variations
Min-Heap Implementation
To convert the above to a min-heap, simply change the comparison operators:
- In
_bubbleUp: Change>to< - In
_sinkDown: Change>to<
Priority Queue with Custom Comparator
class PriorityQueue {
constructor(comparator = (a, b) => a - b) {
this.heap = [];
this.comparator = comparator; // Returns negative if a < b
}
push(val) {
this.heap.push(val);
this._bubbleUp(this.heap.length - 1);
}
pop() {
if (this.heap.length === 1) return this.heap.pop();
if (this.heap.length === 0) return null;
const root = this.heap[0];
this.heap[0] = this.heap.pop();
this._sinkDown(0);
return root;
}
peek() {
return this.heap.length > 0 ? this.heap[0] : null;
}
isEmpty() {
return this.heap.length === 0;
}
_bubbleUp(idx) {
const parentIdx = Math.floor((idx - 1) / 2);
if (parentIdx >= 0 && this.comparator(this.heap[idx], this.heap[parentIdx]) < 0) {
[this.heap[idx], this.heap[parentIdx]] = [this.heap[parentIdx], this.heap[idx]];
this._bubbleUp(parentIdx);
}
}
_sinkDown(idx) {
const leftIdx = 2 * idx + 1;
const rightIdx = 2 * idx + 2;
let bestIdx = idx;
if (leftIdx < this.heap.length && this.comparator(this.heap[leftIdx], this.heap[bestIdx]) < 0) {
bestIdx = leftIdx;
}
if (rightIdx < this.heap.length && this.comparator(this.heap[rightIdx], this.heap[bestIdx]) < 0) {
bestIdx = rightIdx;
}
if (bestIdx !== idx) {
[this.heap[idx], this.heap[bestIdx]] = [this.heap[bestIdx], this.heap[idx]];
this._sinkDown(bestIdx);
}
}
}import heapq
class PriorityQueue:
def __init__(self, comparator=None):
self.heap = []
self.comparator = comparator
def push(self, val):
if self.comparator:
heapq.heappush(self.heap, (self.comparator(val), val))
else:
heapq.heappush(self.heap, val)
def pop(self):
if self.comparator:
return heapq.heappop(self.heap)[1]
else:
return heapq.heappop(self.heap)
def peek(self):
if self.comparator:
return self.heap[0][1] if self.heap else None
else:
return self.heap[0] if self.heap else None
def is_empty(self):
return len(self.heap) == 0import java.util.PriorityQueue;
import java.util.Comparator;
public class PriorityQueue<T> {
private PriorityQueue<T> heap;
public PriorityQueue() {
this.heap = new PriorityQueue<>();
}
public PriorityQueue(Comparator<T> comparator) {
this.heap = new PriorityQueue<>(comparator);
}
public void push(T val) {
heap.offer(val);
}
public T pop() {
return heap.poll();
}
public T peek() {
return heap.peek();
}
public boolean isEmpty() {
return heap.isEmpty();
}
}#include <queue>
#include <functional>
template<typename T, typename Compare = std::less<T>>
class PriorityQueue {
private:
std::priority_queue<T, std::vector<T>, Compare> heap;
public:
void push(const T& val) {
heap.push(val);
}
T pop() {
T val = heap.top();
heap.pop();
return val;
}
T peek() {
return heap.top();
}
bool empty() {
return heap.empty();
}
};package main
import "container/heap"
type PriorityQueue struct {
items []int
minHeap bool
}
func NewPriorityQueue(minHeap bool) *PriorityQueue {
return &PriorityQueue{
items: make([]int, 0),
minHeap: minHeap,
}
}
func (pq *PriorityQueue) Push(val int) {
pq.items = append(pq.items, val)
pq.bubbleUp(len(pq.items) - 1)
}
func (pq *PriorityQueue) Pop() int {
if len(pq.items) == 0 {
return 0
}
val := pq.items[0]
pq.items[0] = pq.items[len(pq.items)-1]
pq.items = pq.items[:len(pq.items)-1]
pq.sinkDown(0)
return val
}
func (pq *PriorityQueue) Peek() int {
if len(pq.items) == 0 {
return 0
}
return pq.items[0]
}
func (pq *PriorityQueue) bubbleUp(idx int) {
parentIdx := (idx - 1) / 2
if parentIdx >= 0 {
shouldSwap := false
if pq.minHeap {
shouldSwap = pq.items[idx] < pq.items[parentIdx]
} else {
shouldSwap = pq.items[idx] > pq.items[parentIdx]
}
if shouldSwap {
pq.items[idx], pq.items[parentIdx] = pq.items[parentIdx], pq.items[idx]
pq.bubbleUp(parentIdx)
}
}
}
func (pq *PriorityQueue) sinkDown(idx int) {
leftIdx := 2*idx + 1
rightIdx := 2*idx + 2
targetIdx := idx
if leftIdx < len(pq.items) {
if pq.minHeap {
if pq.items[leftIdx] < pq.items[targetIdx] {
targetIdx = leftIdx
}
} else {
if pq.items[leftIdx] > pq.items[targetIdx] {
targetIdx = leftIdx
}
}
}
if rightIdx < len(pq.items) {
if pq.minHeap {
if pq.items[rightIdx] < pq.items[targetIdx] {
targetIdx = rightIdx
}
} else {
if pq.items[rightIdx] > pq.items[targetIdx] {
targetIdx = rightIdx
}
}
}
if targetIdx != idx {
pq.items[idx], pq.items[targetIdx] = pq.items[targetIdx], pq.items[idx]
pq.sinkDown(targetIdx)
}
}class PriorityQueue
def initialize(comparator = ->(a, b) { a <=> b })
@heap = []
@comparator = comparator
end
def push(val)
@heap << val
bubble_up(@heap.length - 1)
end
def pop
return @heap.pop if @heap.length == 1
return nil if @heap.empty?
root = @heap[0]
@heap[0] = @heap.pop
sink_down(0)
root
end
def peek
@heap[0] if @heap.any?
end
def empty?
@heap.empty?
end
private
def bubble_up(idx)
parent_idx = (idx - 1) / 2
if parent_idx >= 0 && @comparator.call(@heap[idx], @heap[parent_idx]) < 0
@heap[idx], @heap[parent_idx] = @heap[parent_idx], @heap[idx]
bubble_up(parent_idx)
end
end
def sink_down(idx)
left_idx = 2 * idx + 1
right_idx = 2 * idx + 2
best_idx = idx
if left_idx < @heap.length && @comparator.call(@heap[left_idx], @heap[best_idx]) < 0
best_idx = left_idx
end
if right_idx < @heap.length && @comparator.call(@heap[right_idx], @heap[best_idx]) < 0
best_idx = right_idx
end
if best_idx != idx
@heap[idx], @heap[best_idx] = @heap[best_idx], @heap[idx]
sink_down(best_idx)
end
end
endPractice
Ready to apply these templates? Head to our Practice Problems page to work through real coding challenges that use these heap and priority queue patterns.