PrepStack · Markdown HTML

Top-K bounded heap

Heap, selection, and stream · HTML

Formal shape

For a stream x1..xn with a total ordering key(x), retain the largest k. A min-heap H of capacity k contains exactly the largest min(k,i) items after prefix i. Reverse the boundary for the smallest k.

Recognition signals

False friends

SignalWhy it is different
One offline kth queryWhen mutation is acceptable, Quickselect offers expected O(n); the heap pays log k for streaming, predictable behavior, and input preservation.
A single sliding-window extremeA monotonic deque is usually linear and expires elements directly; a heap needs lazy stale-entry removal.
The complete sorted order is requiredA heap exposes a boundary, not a total order. Sorting everything is clearer when every rank is consumed.

Core invariant

After every input prefix, the heap contains the best at most k candidates from that prefix, and its root is the retained candidate most eligible for eviction.

State, transition, and processing order

ItemDefinition
StateA capacity-k heap whose entry carries a ranking key, a stable tie-breaker, and the original object.
TransitionPush while under capacity. Once full, replace the root only when the new entry is better; otherwise discard it.
Frontier / orderThe source is scanned in arbitrary order. The root is the current admission boundary; sort only the final k items if presentation order matters.

Python skeleton

from heapq import heappush, heapreplace

def top_k(items, k, key=lambda x: x):
    if k <= 0:
        return []
    heap = []
    for serial, item in enumerate(items):
        entry = (key(item), serial, item)
        if len(heap) < k:
            heappush(heap, entry)
        elif entry[:2] > heap[0][:2]:
            heapreplace(heap, entry)
    return [item for _, _, item in sorted(heap, reverse=True)]

Correctness sketch

  1. The invariant holds for the empty prefix.
  2. Before capacity, every seen item belongs. At capacity, an item no better than the root cannot enter the prefix Top-K; a better item can only displace that weakest member.
  3. Induction over all items proves exact membership. The final sort changes order, not membership.

Complexity

TimeSpaceParameters
O(n log k), plus O(k log k) for ordered outputO(k)n is the item count and k is the retention limit.

Edge cases

Error patterns

PatternWhy it fails / fix
Using the wrong heap directionLargest-k needs a min-heap so the weakest retained item is exposed; smallest-k needs the symmetric maximum boundary.
Letting Python compare payloadsEqual tuple prefixes reach the object field. Add a monotone serial tie-breaker before the payload.
Treating heap storage as sortedOnly the root has a rank guarantee; explicitly sort the retained k items when needed.
Push-pop for every rejected itemCompare with the boundary first and use heapreplace only for admitted candidates.

Selection and exclusion rules

RelationTemplateBoundary
orders the frontierTopological sort / indegree eliminationExecutable tasks must also follow a priority rule.
generalizationTwo heaps for streaming medianThe live query is a median boundary rather than a fixed one-sided Top-K.

Original micro-example

PartDetail
ResultThe retained set is {6,7,9}, with admission boundary 6.
SetupReview scores arrive as 6,2,9,7,5 and only the best three are displayed.
TraceAfter [2,6,9], score 7 replaces boundary 2; score 5 fails to beat the new boundary 6.

Recall prompts

My notes