When a problem asks you to repeatedly pull the smallest or largest item, a heap is often the right tool. A heap is a binary tree that maintains a heap property: each parent is ordered with respect to its children (min‑heap for smallest‑first, max‑heap for largest‑first). This guarantees that the root always holds the extreme element, and insertions or removals cost O(log n).
Why a Heap Beats a Sorted List
- Insertion: Adding to a sorted list is O(n), while a heap does it in O(log n).
- Extraction: Getting the top element from a sorted list is O(1), but removing it forces a reshuffle (O(n)). A heap removes the root in O(log n).
- Space: Both use O(n), but a heap avoids storing the whole sorted order when you only need extremes.
Signals That a Problem Wants a Heap
| Phrase in prompt | Typical heap use |
|---|---|
| “top k” or “largest k” | Keep a min‑heap of size k while scanning the input. |
| “merge m sorted lists” | Push the first element of each list onto a min‑heap and repeatedly pop‑push. |
| “running median” | Maintain two heaps (max for lower half, min for upper half). |
| “schedule / assign resources with earliest deadline” | Use a min‑heap to always pick the earliest deadline. |
| “find the k‑th smallest/largest element” | Turn the array into a heap (heapify) then pop k‑1 times. |
If you see any of these patterns, pause and ask yourself whether you need fast peek or pop of an extreme value while the dataset changes.
A Worked Example: Merging k Sorted Arrays
Suppose you have k sorted integer arrays and you must output a single sorted list. A naïve approach concatenates and sorts, costing O(N log N) where N is the total number of elements. Using a heap reduces it to O(N log k).
import heapq
from typing import List
def merge_k_sorted(arrays: List[List[int]]) -> List[int]:
# Build a min‑heap of the first element from each array
min_heap = []
for i, arr in enumerate(arrays):
if arr: # skip empty arrays
heapq.heappush(min_heap, (arr[0], i, 0))
# (value, array_index, element_index)
result = []
while min_heap:
val, arr_i, elem_i = heapq.heappop(min_heap)
result.append(val)
# If the array still has elements, push the next one
if elem_i + 1 < len(arrays[arr_i]):
next_val = arrays[arr_i][elem_i + 1]
heapq.heappush(min_heap, (next_val, arr_i, elem_i + 1))
return result
Explanation
- Initialization – Push the first element of each non‑empty array onto the heap. The tuple
(value, array_id, index)lets us retrieve the next element after we pop. - Loop – Pop the smallest value, append it to the result, then push the next element from the same array (if any). The heap never holds more than k items, so each push/pop is O(log k).
- Complexity – Building the initial heap is O(k). We perform N pop‑push cycles, each O(log k), yielding O(N log k) time and O(k) extra space.
Common Pitfalls
- Using the wrong heap type – Python’s
heapqis a min‑heap. To simulate a max‑heap, store negative values or wrap items in a custom class. - Neglecting tie‑breakers – When two items have the same priority, the heap may compare the whole tuple, causing unexpected ordering. Include a stable secondary key (e.g., an index) to avoid
TypeError. - Mutating objects after insertion – Changing the priority of an element already in the heap does not rebalance it. Instead, push a new entry and lazily discard the old one when it surfaces.
- Assuming heapify is free –
heapq.heapifyruns in O(n), but it still touches every element. For very large inputs, consider streaming inserts if memory is tight.
Five Practice Problems (with hints)
- Find the Kth Largest Element in an Unsorted Array
- Hint: Keep a min‑heap of size k. Iterate the array, pushing each element. When the heap exceeds k, pop the smallest. The root ends up as the kth largest.
- Sliding Window Maximum
- Hint: Use a max‑heap that stores
(value, index). When the window moves, discard entries whose index is outside the window. The heap top is the current maximum.
- Hint: Use a max‑heap that stores
- Course Schedule with Minimum Time (each course has a duration and prerequisite list)
- Hint: Perform a topological sort while maintaining a min‑heap of courses whose prerequisites are satisfied. Always pick the shortest‑duration course next.
- Online Median (stream of numbers, output median after each insertion)
- Hint: Maintain two heaps: a max‑heap for the lower half and a min‑heap for the upper half. Balance their sizes so the median is either the top of one heap or the average of both tops.
- Connecting Cities with Minimum Cost (given a list of possible roads with costs)
- Hint: This is a classic Minimum Spanning Tree problem. Use Prim’s algorithm with a min‑heap of candidate edges. Each pop adds the cheapest edge that connects a new city.
How to practice this
- Write the core heap operations from scratch – Implement
push,pop, andheapifywithout usingheapq. This solidifies the underlying invariants. - Solve one of the practice problems without looking at solutions – Time yourself, then compare your approach to the hint. Refactor any ad‑hoc code into reusable helper functions.
- Run a mock interview – Use Call Assistant to record yourself explaining the solution aloud. It can keep the conversation on track and remind you to tie the explanation back to a relevant project on your resume.
FAQ
- When should I prefer a heap over sorting? Use a heap when you need repeated access to the minimum or maximum while the dataset changes, or when you only need a subset (e.g., top k) rather than a fully sorted list.
- Can I use a heap for non‑numeric priorities?
Yes. As long as the items are comparable (or you provide a key), you can store tuples like
(priority, payload). - Is a heap always faster than a balanced BST? For simple insert‑pop scenarios, a heap’s lower constant factor often wins. However, a BST offers ordered iteration and range queries that a heap does not.
- What is the memory overhead of Python’s heapq? It stores the elements in a plain list, so the overhead is the same as any list plus the tuple wrappers you add for tie‑breakers.
Frequently asked questions
When should I prefer a heap over sorting?
Use a heap when you need repeated access to the smallest or largest element while the collection is being updated, or when you only need a subset like the top k. Sorting is cheaper only if you need the entire list in order once.
Can I use a heap for non‑numeric priorities?
Yes. Store a comparable key (string, date, custom object) as the first element of the tuple you push onto the heap. The heap orders by that key.
Is a heap always faster than a balanced BST?
Not always. For pure insert‑pop of extremes, a heap usually has lower constants. A balanced BST provides ordered traversal and range queries that a heap cannot do.
What is the memory overhead of Python’s heapq?
heapq uses a plain list, so its memory is essentially the list of elements plus any tuple wrappers you add for tie‑breaking. There is no extra node structure.
#coding pattern#heaps#priority queues#interview prep#python#heaps and priority queues