Heaps & priority queues — across Python, Go, TypeScript & C++

A heap is for repeatedly asking “what is the smallest priority right now?” Build is O(n), peek is O(1), push/pop are O(log n). It does not keep the whole collection sorted.

Min-heap core operations

operationPythonGoTypeScriptC++
create import heapq
h = []
type IntHeap []int
func (h IntHeap) Less(i, j int) bool { return h[i] < h[j] }
// Len, Swap, *Push, *Pop
heap.Init(&h)
// no built-in heap
// MinHeap = array + cmp + sift up/down
const h = new MinHeap<number>((a, b) => a - b)
priority_queue<int, vector<int>, greater<int>> pq;
build from items
avg O(n)
h = nums[:]
heapq.heapify(h)
h := IntHeap(nums)
heap.Init(&h)
const h = MinHeap.from(nums, (a, b) => a - b) priority_queue<int, vector<int>, greater<int>> pq(greater<int>{}, nums);
push
avg O(log n)
heapq.heappush(h, x) heap.Push(&h, x) h.push(x) pq.push(x);
peek min
avg O(1)
h[0] h[0] h.peek() pq.top()
pop min
avg O(log n)
x = heapq.heappop(h) x := heap.Pop(&h).(int) const x = h.pop() int x = pq.top();
pq.pop();
replace root
avg O(log n)
x = heapq.heapreplace(h, y) h[0] = y
heap.Fix(&h, 0)
const x = h.replace(y) pq.pop();
pq.push(y);

Priorities, max-heaps, and tie-breaks

operationPythonGoTypeScriptC++
max-heap
same costs
heapq.heappush(h, -x)
x = -heapq.heappop(h)
// reverse Less
func (h IntHeap) Less(i, j int) bool { return h[i] > h[j] }
const h = new MinHeap<number>((a, b) => b - a) priority_queue<int> pq;
// max-heap by default
priority item
avg O(log n)
heapq.heappush(h, (pri, seq, item))
pri, seq, item = heapq.heappop(h)
type Item struct { pri, seq int; val string }
// Less: pri, then seq
type Item = { pri: number; seq: number; val: string }
new MinHeap<Item>((a, b) => a.pri - b.pri || a.seq - b.seq)
using T = tuple<int, int, string>;
priority_queue<T, vector<T>, greater<T>> pq;
push then pop
avg O(log n)
x = heapq.heappushpop(h, y) heap.Push(&h, y)
x := heap.Pop(&h).(int)
h.push(y)
const x = h.pop()
pq.push(y);
int x = pq.top(); pq.pop();

Two interview patterns

patternPythonGoTypeScriptC++
top-K largest
avg O(n log k), space O(k)
def top_k(nums, k):
  h = []
  for x in nums:
    if len(h) < k: heapq.heappush(h, x)
    elif x > h[0]: heapq.heapreplace(h, x)
  return h
// keep min-heap size k
if h.Len() < k { heap.Push(&h, x) }
else if x > h[0] { h[0] = x; heap.Fix(&h, 0) }
if (h.size() < k) h.push(x)
else if (x > h.peek()!) h.replace(x)
if (pq.size() < k) pq.push(x);
else if (x > pq.top()) { pq.pop(); pq.push(x); }
running median
add O(log n), median O(1)
low, high = [], []
heapq.heappush(low, -x)
heapq.heappush(high, -heapq.heappop(low))
if len(high) > len(low):
  heapq.heappush(low, -heapq.heappop(high))
m = -low[0] if len(low) > len(high) else (-low[0] + high[0]) / 2
// low: max-heap, high: min-heap
// push, move top across, rebalance sizes
const low = new MinHeap<number>((a, b) => b - a)
const high = new MinHeap<number>((a, b) => a - b)
priority_queue<int> low;
priority_queue<int, vector<int>, greater<int>> high;

Gotchas

PythonGoTypeScriptC++
  • heapq gives functions, not methods: write heapq.heappush(h, x).
  • For a max-heap, store -x and negate again when popping.
  • For records, prefer (priority, tiebreak, item).
  • Implement heap.Interface: Len, Less, Swap, Push, Pop.
  • Push and Pop need pointer receivers when resizing the slice.
  • heap.Pop returns any; type assert it.
  • No standard heap; bring a binary-heap class or use sorted arrays only for small inputs.
  • Array.sort() sorts strings by default; numbers need (a, b) => a - b.
  • shift() is O(n), so sorted-array pops are costly.
  • std::priority_queue is a max-heap by default.
  • pop() returns nothing; read top() first.
  • No cheap arbitrary delete/decrease-key; push a new entry and ignore stale ones.

Official docs: https://docs.python.org/3/library/heapq.html, https://pkg.go.dev/container/heap, https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/sort, https://en.cppreference.com/w/cpp/container/priority_queue.