Skip to Content
Course content

234: The heapq Module for Priority Queues

Click on the "Edit" button in the top corner of the screen to edit your slide content.

Listen, I've seen this happen in dozens of code reviews: a developer wants a priority queue, they find the heapq module, and then they get frustrated because the list they're looking at doesn't look sorted. They'll say, "I pushed the items in, but the list is still a mess!"

The "It's Just a Sorted List" Trap

The biggest misconception about heapq is that it maintains a fully sorted list. It doesn't. If you push five numbers into a heap and then print the list, you'll likely see something that looks almost random. Let me show you exactly what I mean.

import heapq

tasks = []
heapq.heappush(tasks, 10)
heapq.heappush(tasks, 1)
heapq.heappush(tasks, 5)

print(tasks) 
# You might expect [1, 5, 10], but you'll actually get [1, 10, 5]

At first glance, [1, 10, 5] looks wrong. But it's not. heapq implements a min-heap. The only absolute guarantee it gives you is that the smallest element is always at the front (index 0). The rest of the elements are arranged in a binary tree structure that makes it incredibly efficient to find the next smallest item, but it doesn't waste time sorting the entire list every time you add something.

The Min-Heap Reality

To actually get the elements in sorted order, you have to pop them. This is where the magic happens. When you call heappop(), Python removes the smallest item and then reshuffles the remaining elements just enough to ensure the new smallest item is back at index 0.

import heapq

# Let's use that same "messy" list from before
tasks = [1, 10, 5] 
heapq.heapify(tasks) # Ensures the list satisfies the heap property

while tasks:
    print(heapq.heappop(tasks))

# Output:
# 1
# 5
# 10

I usually tell my juniors to think of a heap as a "partially ordered" structure. It's a trade-off. Sorting a list takes O(N log N) time. Inserting into a heap only takes O(log N). If you only ever need the "best" or "smallest" item and don't care about the order of the rest, using a heap is significantly faster.

Handling Custom Priorities with Tuples

In the real world, you aren't just storing integers. You're storing objects—tasks, users, or packets. Since heapq sorts based on the first element of a tuple, the standard pattern is to store your data as (priority, data).

import heapq

# (Priority, Task Name) - Lower number means higher priority
queue = []
heapq.heappush(queue, (3, "Clean the kitchen"))
heapq.heappush(queue, (1, "Stop the server leak"))
heapq.heappush(queue, (2, "Email the client"))

while queue:
    priority, task = heapq.heappop(queue)
    print(f"Processing {task} (Priority: {priority})")

# Output:
# Processing Stop the server leak (Priority: 1)
# Processing Email the client (Priority: 2)
# Processing Clean the kitchen (Priority: 3)

One quick warning: if two items have the same priority, Python will try to compare the second element of the tuple (the task name in this case). If the second element is something that can't be compared (like a custom class instance), your code will crash. If that happens, I recommend adding a unique counter as a tie-breaker: (priority, count, task).

When to Use a Heap Over Sorting

You might be wondering, "Why not just call .sort() every time I add a task?" I've tried that in production—don't. If you have a list of 10,000 items and you add one new item, .sort() has to look at everything. A heap only has to perform a few swaps to move that new item into the correct relative position. Use heapq whenever you have a dynamic stream of data and you constantly need access to the minimum (or maximum) element.




📋 Practical Task

Building an Emergency Room Triage System

You need to build a triage system for a hospital. Patients arrive at different times with different urgency levels (1 being critical, 5 being non-urgent). You must ensure that the most critical patients are seen first, regardless of when they arrived.

Requirements:

  • Create a list called triage_queue.
  • Implement a function add_patient(name, urgency) that pushes a tuple of (urgency, name) onto the heap.
  • Implement a function treat_patient() that pops the highest priority patient from the heap and returns a string: "Treating [name]". If the queue is empty, return "No patients in queue".

Test Case:

add_patient("John Doe", 3)
add_patient("Jane Smith", 1)
add_patient("Bob Brown", 2)
print(treat_patient()) # Should be Jane Smith
print(treat_patient()) # Should be Bob Brown
Rating
0 0

There are no comments for now.

to be the first to leave a comment.