Card 06/ 09
Treating Python's heapq as a Max-Heap
heapq always pops the smallest item, so a largest-first queue needs negated keys.
Python's heapq functions always keep the smallest item at the front of the list, so code that pushes scores and expects heappop to return the highest one gets the lowest instead. This card covers that mistake, the usual fix of pushing negated keys, and the matching fix in Java, whose PriorityQueue also pops the smallest item unless told otherwise.
A heap is a tree stored in an array that keeps one extreme item at its root: a min-heap keeps the smallest, a max-heap the largest. heapq treats an ordinary list as a min-heap and offers no switch to reverse it. The mistake is natural because a leaderboard, a list of the most urgent jobs or the k largest values all want largest first, and nothing in the names heappush and heappop says which end comes out.
Each listing below pushes the scores 40, 95, 72 and 88. The first part shows the mistake: the pop returned 40. The second part is the fix. Pushing -score turns the largest score into the smallest key, so heappop returns it first, and negating again on the way out restores the score. When each item is a tuple, Python compares tuples position by position, so (-score, name) orders by score, highest first, and breaks ties by name.
Expecting the largest score from a min-heap, then the fix, in each language
import heapq
import sys
scores = [40, 95, 72, 88]
wrong = []
for score in scores:
heapq.heappush(wrong, score)
print("expected the largest, got:", heapq.heappop(wrong))
negated = []
for score in scores:
heapq.heappush(negated, -score)
print("negated keys, largest first:", [-heapq.heappop(negated) for _ in range(len(scores))])
players = [("Ana", 40), ("Ben", 95), ("Caro", 72), ("Dev", 88)]
ranked = []
for name, score in players:
heapq.heappush(ranked, (-score, name))
top_score, top_name = heapq.heappop(ranked)
print("tuple keys, top player:", top_name, -top_score)
print("Python", sys.version.split()[0], "has heappush_max:", hasattr(heapq, "heappush_max"))
if hasattr(heapq, "heappush_max"):
native = []
for score in scores:
heapq.heappush_max(native, score)
print("heappop_max order:", [heapq.heappop_max(native) for _ in range(len(scores))])expected the largest, got: 40
negated keys, largest first: [95, 88, 72, 40]
tuple keys, top player: Ben 95
Python 3.14.8 has heappush_max: True
heappop_max order: [95, 88, 72, 40]import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
import java.util.PriorityQueue;
public class MaxHeap {
public static void main(String[] args) {
int[] scores = {40, 95, 72, 88};
PriorityQueue<Integer> wrong = new PriorityQueue<>();
for (int score : scores) {
wrong.add(score);
}
System.out.println("expected the largest, got: " + wrong.poll());
PriorityQueue<Integer> largestFirst = new PriorityQueue<>(Comparator.reverseOrder());
for (int score : scores) {
largestFirst.add(score);
}
List<Integer> order = new ArrayList<>();
while (!largestFirst.isEmpty()) {
order.add(largestFirst.poll());
}
System.out.println("reverseOrder, largest first: " + order);
}
}expected the largest, got: 40
reverseOrder, largest first: [95, 88, 72, 40]In Java the fix is a comparator: new PriorityQueue<>(Comparator.reverseOrder()) polls the largest item first, and the run polled 95, 88, 72, 40.
Python 3.14 added public max-heap functions, among them heapq.heappush_max and heapq.heappop_max, and the run on Python 3.14.8 used them. Code that has to run on an earlier version, or on keys that cannot be negated, such as strings, still needs another route: negation for numbers, or a small wrapper class whose comparison is reversed.
To spot the mistake, look for heappop where the surrounding code talks about the largest, the highest or the top, and check whether the keys were negated on the way in.
← → move through sections and cards