Interview Cracker GitHub

Pattern: Heap / Top-K

A heap gives you the smallest (or largest) element in O(1) and maintains that property in O(log n) per insert or removal. Reach for it when you repeatedly need the extreme element of a changing collection.

When to reach for it

The counter-intuitive part

To track the K LARGEST elements, use a MIN-heap of size K.

The root is then the weakest of your current champions — exactly the one to compare against and evict. Keeping a max-heap would put the strongest at the root, which tells you nothing about what to drop.

import heapq

heap = []                          # min-heap of the best K so far
for item in stream:
    if len(heap) < k:
        heapq.heappush(heap, item)
    elif item > heap[0]:           # better than the weakest kept
        heapq.heapreplace(heap, item)

O(n log k) time, O(k) space. Compare with sorting: O(n log n) time and O(n) space. When k is small and n is huge — or n does not fit in memory — that difference is the whole point.

Python specifics

heapq is a min-heap only. For a max-heap, push negated values and negate on the way out. For tuples, it compares element by element, so (priority, item) sorts by priority — but a tie then compares item, which raises TypeError if the items are not comparable. Insert a unique counter as a tiebreaker: (priority, count, item).

Operation Cost
heap[0] (peek) O(1)
heappush / heappop O(log n)
heapreplace (pop then push) O(log n), one sift
heapify(list) O(n), not O(n log n)
find an arbitrary element O(n) — a heap is only partially ordered

Do not confuse a heap with a sorted structure

A heap knows its minimum and nothing else. It cannot iterate in order, cannot do range queries, and cannot find an arbitrary element quickly. If you need those, use a balanced tree.

Challenges

File Difficulty Shape
top_k_frequent_elements.py 🟡 Medium Count, then min-heap of size k
merge_k_sorted_lists.py 🟡 Medium K-way merge, heap of size k
kth_largest_element.py 🟡 Medium Min-heap of size k, evict the weakest