Heaps in Python: heapq as a Priority Queue, and Finding the K Largest
Python’s heapq module turns a plain list into a binary min-heap in place, where heap[0] is always the smallest element. This post heapifies a list and pops it in order, then writes a k_largest function using a size-k min-heap and checks it against the standard library’s heapq.nlargest.
The code
import heapq
def k_largest(nums, k):
# Keep a min-heap of size k: the smallest of the k largest seen so far
# sits at heap[0]. Any new number bigger than that gets swapped in.
heap = nums[:k]
heapq.heapify(heap)
for n in nums[k:]:
if n > heap[0]:
heapq.heapreplace(heap, n)
return sorted(heap, reverse=True)
nums = [15, 3, 42, 8, 23, 4, 16, 99, 1, 7]
print("=== heapq as a priority queue ===")
heap = nums[:]
heapq.heapify(heap)
print("original list:", nums)
print("after heapify, heap[0] (smallest):", heap[0])
popped = []
while heap:
popped.append(heapq.heappop(heap))
print("popped one at a time:", popped)
print("\n=== k-largest via a size-k min-heap ===")
for k in [1, 3, 5]:
print(f"k_largest(nums, {k}) ->", k_largest(nums, k))
print("\n=== heapq.nlargest for comparison ===")
for k in [1, 3, 5]:
print(f"heapq.nlargest({k}, nums) ->", heapq.nlargest(k, nums))
heapq.heapify rearranges heap in place so heap[0] is the minimum; repeated heappop calls drain it in ascending order. k_largest seeds a size-k heap from the first k numbers, then for every remaining number swaps it in via heapreplace whenever it beats the current minimum of that heap.
Running it
Real output:
=== heapq as a priority queue ===
original list: [15, 3, 42, 8, 23, 4, 16, 99, 1, 7]
after heapify, heap[0] (smallest): 1
popped one at a time: [1, 3, 4, 7, 8, 15, 16, 23, 42, 99]
=== k-largest via a size-k min-heap ===
k_largest(nums, 1) -> [99]
k_largest(nums, 3) -> [99, 42, 23]
k_largest(nums, 5) -> [99, 42, 23, 16, 15]
=== heapq.nlargest for comparison ===
heapq.nlargest(1, nums) -> [99]
heapq.nlargest(3, nums) -> [99, 42, 23]
heapq.nlargest(5, nums) -> [99, 42, 23, 16, 15]
After heapify, heap[0] was 1, the smallest value in the list, and popping one at a time produced [1, 3, 4, 7, 8, 15, 16, 23, 42, 99] — fully ascending. The hand-written k_largest matched heapq.nlargest exactly at every tested k (1, 3, and 5): both returned [99], [99, 42, 23], and [99, 42, 23, 16, 15].
Takeaway
The run showed heapq.heapify + repeated heappop draining a list in sorted order, and a size-k min-heap approach to k_largest producing output identical to heapq.nlargest across k=1, 3, and 5 on the same input.