AI Skill Report Card
Using Heapq for Priority Queues
Quick Start14 / 15
Pythonimport heapq # Build a min-heap in place from a list data = [5, 7, 9, 1, 3] heapq.heapify(data) # data is now heap-ordered, data[0] is smallest heapq.heappush(data, 4) # push new item, maintain heap invariant smallest = heapq.heappop(data) # pop and return smallest item # Get n largest/smallest without fully sorting nums = [1, 8, 2, 23, 7, -4, 18, 23, 42, 37, 2] print(heapq.nlargest(3, nums)) # [42, 37, 23] print(heapq.nsmallest(3, nums)) # [-4, 1, 2]
Recommendation▾
Add an example showing lazy deletion pattern in full working code, since it's referenced twice but never demonstrated
Workflow14 / 15
Progress:
- Step 1: Decide if you need a full priority queue, a one-off top-k query, or a merge operation
- Step 2: Choose the right heapq function(s) for the task
- Step 3: For priority queues with ties/mutability, wrap items as tuples
(priority, counter, item) - Step 4: For max-heap behavior, negate priorities (heapq only implements min-heap)
- Step 5: Test edge cases — empty heap, equal priorities, updating/removing entries
Core functions:
| Function | Purpose |
|---|---|
heapify(x) | Transform list into a heap, in-place, O(n) |
heappush(heap, item) | Push item, maintain invariant, O(log n) |
heappop(heap) | Pop smallest item, O(log n) |
heappushpop(heap, item) | Push then pop, more efficient than separate calls |
heapreplace(heap, item) | Pop then push, faster but pop happens before push (heap never empty briefly) |
merge(*iterables, key=None, reverse=False) | Merge sorted inputs into single sorted generator |
nlargest(n, iterable, key=None) | n largest elements, use when n is small relative to iterable |
nsmallest(n, iterable, key=None) | n smallest elements |
Choosing between approaches:
n == 1: usemin()/max()— fastern small, iterable large: usenlargest/nsmallestn ≈ len(iterable): usesorted(iterable)[:n]— faster than heap approach- Repeated push/pop over time: use
heappush/heappopdirectly on a heap list
Recommendation▾
Consider a brief note on time complexity comparison table to reinforce the 'choosing between approaches' section
Examples18 / 20
Example 1: Priority queue with stable ordering and tie-breaking
Input: Tasks with priorities, need FIFO order for equal priorities
Pythonimport heapq import itertools counter = itertools.count() pq = [] def add_task(task, priority=0): entry = (priority, next(counter), task) heapq.heappush(pq, entry) def pop_task(): priority, count, task = heapq.heappop(pq) return task add_task('write code', priority=2) add_task('write spec', priority=1) add_task('create tests', priority=1)
Output: pop_task() calls return 'write spec', 'create tests', 'write code' — priority order, FIFO within same priority.
Example 2: K largest elements from a stream
Input: stream = [3, 1, 4, 1, 5, 9, 2, 6], need top 3
Pythonimport heapq print(heapq.nlargest(3, stream))
Output: [9, 6, 5]
Example 3: Merging multiple sorted logs by timestamp
Input: Several already-sorted iterables of log entries
Pythonimport heapq log1 = [1, 4, 7] log2 = [2, 5, 8] log3 = [3, 6, 9] merged = list(heapq.merge(log1, log2, log3))
Output: [1, 2, 3, 4, 5, 6, 7, 8, 9]
Example 4: Max-heap via negation
Input: Need largest-first pop behavior
Pythonimport heapq nums = [3, 1, 4, 1, 5, 9] heap = [-n for n in nums] heapq.heapify(heap) largest = -heapq.heappop(heap) # 9
Output: largest == 9
Recommendation▾
Could add a short example combining heapq with a custom class using __lt__ for object priority queues
Best Practices
- Use tuples
(priority, item)for ordered heap entries; ensure items are comparable or add a tiebreaker (e.g., a counter) to avoid comparing non-orderable items when priorities tie. - For max-heaps, negate numeric priorities rather than writing a custom comparator (heapq has no
key/reverseparam for push/pop). heapify()is O(n) — always prefer it over repeatedheappushcalls when building from an existing list.- Use
heapreplaceinstead ofheappop+heappushwhen you always want to maintain a fixed heap size (e.g., sliding window top-k). - For mutable/updatable priority queues (need to change priority of existing item), mark old entries as invalid and push new one instead of trying to remove mid-heap — heaps don't support efficient arbitrary removal.
heapq.merge()is lazy (returns iterator) — good for merging large sorted files/streams without loading everything into memory.
Common Pitfalls
- Don't assume the heap list is fully sorted — only
heap[0]is guaranteed to be the smallest; useheapq.heapify+ repeatedheappop, or justsorted(), if you need a fully sorted result. - Don't use
heapqfor max-heap directly — there's no built-in max-heap; negate values or wrap in a class with reversed__lt__. - Don't call
heappush/heappopon a list that wasn't heapified first — behavior is undefined on non-heap-ordered lists. - Don't try to update priority of an existing item in place — mutating a heap element doesn't re-sort the heap; use the "lazy deletion" pattern (push new entry, mark old as removed, skip removed entries on pop).
- Avoid
nlargest/nsmallestwhennis close to the full length of the iterable — plainsorted()is more efficient in that case. - Remember comparisons happen on full tuples — if priorities tie and items aren't comparable (e.g., dicts), you'll get a
TypeError; always include a unique tiebreaker.