Heaps & Priority Queues

Heaps & Priority Queues

The mental hook for this week: a heap always keeps its most extreme element ready to grab. So whenever a problem says “k largest,” “k closest,” or “next by priority,” a heap is usually the move.

🧠 What a heap is

A binary heap is a complete binary tree that follows one rule:

  • Min-heap: every parent is smaller than or equal to its children, so the smallest value sits at the root.
  • Max-heap: every parent is larger, so the largest value sits at the root.

Because it’s a complete tree, it packs neatly into an array and the parent/child relationships are just index math:

parent(i) = (i - 1) // 2
left(i)   = 2*i + 1
right(i)  = 2*i + 2

📊 Costs

OperationTimeNote
Peek min/maxO(1)It’s just the root
PushO(log n)Bubble up
Pop min/maxO(log n)Swap root down, bubble down
Build (heapify)O(n)Bottom-up, better than n log n

The heapify one surprised me: building a heap from an existing array is O(n), not O(n log n). Good fact to have in your back pocket.

🐍 Python only gives you a min-heap

heapq is a min-heap, full stop. Want a max-heap? Push the negatives and flip the sign back when you read them.

import heapq
h = []
heapq.heappush(h, 5)
heapq.heappush(h, 1)
smallest = heapq.heappop(h)   # 1

# max-heap by negating
heapq.heappush(h, -value)
largest = -heapq.heappop(h)

🧩 Top-K with a size-K heap

The idea that felt clever the first time: to find the k largest elements, keep a min-heap of size k. The smallest of your current top k sits at the top, so if a new number beats it, swap it in.

import heapq

def k_largest(nums, k):
    heap = nums[:k]
    heapq.heapify(heap)               # O(k)
    for n in nums[k:]:
        if n > heap[0]:
            heapq.heapreplace(heap, n)
    return heap

That’s O(n log k), which beats sorting’s O(n log n) whenever k is small. If you only want a few, don’t sort everything.

🌊 The two-heap median

For a running median over a stream, keep two heaps: a max-heap for the lower half and a min-heap for the upper half, balanced in size. The median is then O(1) to read and each insert is O(log n). This is the template for Find Median from Data Stream, and I go deeper on it in the Phase 2 heap notes.

⚠️ Things that bit me

  • Forgetting heapq is min-only and getting the wrong extreme.
  • Pushing tuples like (priority, item) and having it blow up when two priorities tie and the items aren’t comparable. Add a tie-break counter.
  • Sorting the whole array when I only needed the top k.

🏋️ Problems I did this week

📝 What I want to remember

Heap gives O(1) peek and O(log n) push/pop of the extreme element. Top-K means a size-k heap and O(n log k). Two balanced heaps track a stream’s median.