Top K Elements (Min-Heap of Size K)
You're given an integer array nums and an integer k. Report the k values that appear most often in nums — ties in return order don't matter, so any ordering of the k values is accepted.
Follow-up: a full O(n log n) sort is accepted, but can you do better? Aim for a time complexity below O(n log n), where n is the length of nums.
1 appears 3 times and 2 twice, more often than 3.Top-K Heap
stream of N elements ➔ keep size-K min-heap ➔ O(N log K) filtering| 1 | import heapq |
| 2 | import itertools |
| 3 | from collections import Counter |
| 4 | from typing import Callable, Iterable, List, Optional, Tuple, TypeVar |
| 5 | |
| 6 | Item = TypeVar("Item") |
| 7 | |
| 8 | _arrivals = itertools.count() # arrival numbers for keep_k, shared by every call |
| 9 | |
| 10 | |
| 11 | def keep_k(items: Iterable[Item], k: int, key: Callable[[Item], float], min_heap: Optional[List[Tuple[float, int, Item]]] = None) -> List[Item]: |
| 12 | """Top-K Heap: the k items with the largest key(item), in any order. |
| 13 | The heap is a min-heap, so its root is the weakest item kept and the first one to evict. |
| 14 | For a stream (Kth Largest Element in a Stream), pass the same min_heap list on every call with the new items. |
| 15 | Then min_heap[0][2] is the k-th largest item seen so far. |
| 16 | Precondition: k >= 1. For the k smallest keys, pass the negated key. |
| 17 | Maximum Subsequence Score needs the sum of the kept keys at each step, so it keeps its own running sum. |
| 18 | N = the items. T = the cost of one key call. |
| 19 | Time: O(N * (T + log K)) | Space: O(K) heap |
| 20 | """ |
| 21 | if min_heap is None: |
| 22 | min_heap = [] # Min-heap of (key, arrival, item); the root is the weakest item kept |
| 23 | |
| 24 | for item in items: |
| 25 | # 1. Push Every Candidate: |
| 26 | # Each entry is (key, arrival, item); arrival is a number never used before, so two items with an equal key are never compared. |
| 27 | heapq.heappush(min_heap, (key(item), next(_arrivals), item)) |
| 28 | |
| 29 | # 2. Evict the Weakest When Over Capacity: |
| 30 | # A popped item is no bigger than k others already seen, so it can never belong in the final top k. |
| 31 | if len(min_heap) > k: |
| 32 | heapq.heappop(min_heap) |
| 33 | |
| 34 | # 3. Return the Kept Items: |
| 35 | # Every item still in min_heap is one of the k biggest keys seen, in no particular order. |
| 36 | return [item for _, _, item in min_heap] |
min_heap holds the k biggest counts seen so far (fewer at the start), and its root is the smallest of them.To keep the k biggest items, keep a min-heap of size k: push each (count, value) pair, and when min_heap grows past k, pop its root, the weakest item kept. A popped item is no bigger than k others already seen, so it can never be in the top k, and after the last push min_heap holds exactly the k biggest, with the k-th biggest at the root. The trap is the heap direction: the k largest need a min-heap and the k smallest a max-heap, because the root must be the item you evict next.
See it run
Top-K Heap, step by step
len(min_heap) 0root none
The top row is min_heap in its array layout, with the index above each cell. The bottom row lists the evicted entries in order. Each entry is (count, value).
rootthe weakest entry kept, first to evictpushedthe entry added at this steppushed, rootthe entry added at this step, now at the rootevictedan entry popped from min_heapkeptan entry in the final answer
Start: counts = {1: 3, 2: 1, 3: 5, 4: 2, 5: 4, 6: 1} and k = 3. min_heap is empty.
Gotcha 1 of 3
- The trap
Gotcha 1: Keeping the
klargest in a max-heapMistakeheapq.heappush(min_heap, (-count, value))Fixheapq.heappush(min_heap, (count, value))What happens: Python's
heapqis a min-heap only. Pushing-countturns it into a max-heap, so the root is the most frequent item. When the heap grows pastk,heappopthen removes the most frequent item. The heap ends with thekleast frequent values. To keep theklargest, the root must be the smallest item kept, so use a min-heap.Example: Top K Frequent Elements (LC 347): nums = [1, 1, 1, 2, 2, 3], k = 2Mistake: returns [2, 3]Fix: returns [1, 2]
- All numbers are given at once, you need one k-th value and no stream follows, as in Kth Largest Element in an Array (LC 215). See Quickselect
- Every item must come out ordered by its count, and counts can't exceed the input length, as in Sort Characters By Frequency (LC 451), not just the top
k. See Bucket Sort
Deep Dive
Why evicting the root is always safe
min_heap is a priority queue: it lets you read and remove its smallest entry quickly. Each entry is (count, value), so the smallest entry is the value with the smallest count. That entry sits at the root.
Take Top K Frequent Elements (LC 347) with nums = [1, 1, 1, 2, 3, 3, 3, 3, 3, 4, 4, 5, 5, 5, 5, 6] and k = 3. The counts are 1: 3, 2: 1, 3: 5, 4: 2, 5: 4 and 6: 1. The code pushes the six pairs in that order.
After the first three pushes, min_heap holds (3, 1), (1, 2) and (5, 3), and the root is (1, 2). Pushing (2, 4) makes four entries, which is more than k. The code pops the root (1, 2). Three entries remain, and they all have a bigger count than the entry that left.
Why this is correct: an evicted entry has a count no bigger than the k entries that stay in min_heap. So at least k values beat it, and it can never be one of the k most frequent. Later pushes only add more competitors, so the decision never needs to be undone.
The end of the example shows a useful case. The pair (1, 6) is pushed last, and it becomes the root at once. The next line pops it, so a new entry can be evicted in the same step it arrives. The final min_heap holds (3, 1), (4, 5) and (5, 3), so the answer is the values [1, 5, 3] in any order.
What a binary heap is
A binary heap is a list that is read as a tree. The entry at index i has its children at indexes 2 * i + 1 and 2 * i + 2, and its parent at index (i - 1) // 2. In a min-heap, every entry is no bigger than its two children. So the smallest entry is always at index 0, the root.
heappush adds the new entry at the end of the list. Then it swaps the entry with its parent while the entry is smaller than the parent. This is called sifting up, and it takes at most log2(len(min_heap)) swaps. heappop removes index 0, moves the last entry to index 0, and sifts it down. At each level, it swaps with the smaller child until the entry is no bigger than both children. This takes at most log2(len(min_heap)) levels too.
Example: push 5, 3, 8, 1, 2, 9, 4 one at a time into an empty list with heapq.heappush. The list becomes [1, 2, 4, 5, 3, 9, 8]. Index 1 holds 2, and its children at indexes 3 and 4 hold 5 and 3. Both are bigger, so the rule holds. heapq.heappop then returns 1, and the list becomes [2, 3, 4, 5, 8, 9].
heapq.heapify(items) turns a list that already exists into a heap in O(n) time, and it does not need n pushes. It sifts down every entry that has children, starting from index n // 2 - 1 and moving back to index 0. Half of the entries are leaves and need no work. Only one entry sits at the top level, and it may move down all the levels. The sum of all the moves is at most about n. In a test, heapify on 1000 distinct random numbers made 729 swaps, and on 100000 numbers it made 74427 swaps. Pushing 1000 numbers in descending order made 7987 swaps, because each new entry sifts all the way up.
Python's heapq is a min-heap only. For a max-heap, push the negated value and negate it again after each pop. Pushing -5, -3, -8, -1, -2, -9, -4 and popping three times gives 9, 8, 5 after negating. This card needs the min-heap itself: the root must be the entry to evict next.
Three ways to find the top K, and their cost
There are three common ways to take the k biggest of n items. Sorting costs O(n log n). The Top-K Heap costs O(n log k), because each of the n pushes and pops works on a heap of at most k + 1 entries. Counting into buckets costs O(n) when the key is a count that can never be bigger than n.
A comparison count shows the difference. With n = 100000 distinct random numbers and k = 10, Python's sorted made about 1.53 million comparisons, and the push-then-pop loop of this card made about 0.70 million. The loop wins because a heap of 11 entries is only about 3 levels deep.
The same test with k = 1000 gives a different result. The heap loop made about 1.85 million comparisons, which is more than the sort. Big-O hides the constant: each pop compares up to two children per level, so the loop costs about 2 * log2(k) comparisons for a pop. Use the heap when k is much smaller than n, or when the data arrives as a stream and cannot be sorted at once.
For Top K Frequent Elements (LC 347), the key is a count, and a count is at most len(nums). So a list of buckets works: buckets[c] holds the values with count c. Then read the buckets from the largest count down until k values are collected. The code is counts = Counter(nums), then buckets = [[] for _ in range(len(nums) + 1)], then for value, count in counts.items(): buckets[count].append(value). It makes no comparison between two counts, and its time is O(n). On 3000 random inputs, the bucket version, a sort version and the heap version returned the same values.
The buckets need a small, known range of keys. They do not work for the k closest points or the k biggest sums, where the key can be any number. The heap works for any key you can compare, so it is the general choice. The tool card Quickselect finds one k-th value in O(n) on average when every value is in memory.
The stream form, and equal keys
In Kth Largest Element in a Stream (LC 703), numbers arrive one at a time, and the k-th largest is asked after each one. The template keeps min_heap between calls: pass the same list each time. After each push and pop, min_heap[0] is the k-th largest number seen so far. With k = 3 and the numbers 4, 5, 8, 2, the root after the fourth number is 4.
The general keep_k pushes (key(item), next(_arrivals), item). The middle number is an arrival counter. When two items have the same key, Python compares the arrival numbers next, and they are never equal. So Python never has to compare the items themselves. This matters when the items are objects or lists that cannot be compared with <.
The k smallest keys need a max-heap, which in Python means negating the key: key=lambda item: -item. The root is then the biggest of the kept keys, which is the one to evict.
60-Second Pitch
I need the k most frequent values, and k is small. I count the values first, which takes O(n).
Then I keep a min-heap of size k. I push each (count, value) pair, and when the heap holds more than k, I pop the root. The root has the smallest count, so I drop the weakest entry. An entry I drop already has k entries at least as frequent, so it can never be in the answer.
The time is O(n log k), and the space is O(n) for the counts plus O(k) for the heap.
If the counts are at most n, buckets give O(n) with no heap. If the data is a stream, I keep the same heap between calls, and the root is the k-th largest.
A max-heap would be the wrong choice, because it pops the item I want to keep.
Common Questions
- Why a min-heap to keep the largest items?
The item to evict next is the weakest one kept. A min-heap puts the smallest key at the root, so the weakest item is the one
heappopremoves. A max-heap would remove the strongest.- What is the time and space, and why is it not O(n log n)?
Each of the
nitems costs one push and at most one pop. The heap never holds more thank + 1entries, so each operation costs O(log k). The total is O(n log k) time andO(k)space for the heap. With the counts, the space isO(n).- When would you not use a heap?
When
kis close ton, sorting is as cheap and simpler. When the keys are counts that can never exceedn, buckets giveO(n). When you need one k-th value and every value is in memory, Quickselect isO(n)on average.- How do you handle a stream?
Keep
min_heapbetween calls, and push each new number. If the heap holds more thankentries, pop the root. After that,min_heap[0]is the k-th largest number seen so far. This is Kth Largest Element in a Stream (LC 703).- How do you get the k smallest, or break ties?
For the
ksmallest, push the negated key, so the root is the biggest kept key. For ties, push(key, arrival, item)with an arrival number that never repeats, so Python never compares the items.
SWE Reasoning
Spot it
- The question asks for the
klargest, most frequent, closest or best items, andkis much smaller thann. - The data arrives as a stream, and the answer must be ready after every new item.
- Sorting everything would work, but it keeps more than the answer needs.
- The answer can be given in any order.
- Cheapest-first search on a weighted graph uses the same tool: a heap that always gives the smallest entry. Read the Dijkstra's Algorithm card in the Graphs pattern for it.
The invariant, step by step
min_heapstarts empty. It will hold at mostkentries after every step.- Each candidate is pushed as
(count, value), so the heap orders entries by count first. - When
min_heapholds more thankentries, the root is popped. The root has the smallest count, so it is the weakest entry kept. - The popped entry has a count no bigger than
kothers already seen, so it can never be in the topk. - After the last push and pop,
min_heapholds exactly thekbiggest counts, and its root is the k-th biggest.
Traps
- A max-heap (pushing
-count) pops the most frequent entry. The heap ends with thekleast frequent values. For theklargest, the root must be the smallest kept entry. - Pushing
(value, count)orders the heap by value. The code then keeps the biggest values and returns the wrong ones. - Evicting at
len(min_heap) >= kkeeps onlyk - 1entries. The test islen(min_heap) > k. - Equal keys on objects that cannot be compared raise an error when the tuple reaches its last item. An arrival number before the item prevents it.
- Using the heap when
kis close toncosts more than sorting.
Practice
Top K Elements (Min-Heap of Size K)
You're given an integer array nums and an integer k. Report the k values that appear most often in nums — ties in return order don't matter, so any ordering of the k values is accepted.
Follow-up: a full O(n log n) sort is accepted, but can you do better? Aim for a time complexity below O(n log n), where n is the length of nums.
1 appears 3 times and 2 twice, more often than 3.Top-K Heap
stream of N elements ➔ keep size-K min-heap ➔ O(N log K) filtering| 1 | import heapq |
| 2 | import itertools |
| 3 | from collections import Counter |
| 4 | from typing import Callable, Iterable, List, Optional, Tuple, TypeVar |
| 5 | |
| 6 | Item = TypeVar("Item") |
| 7 | |
| 8 | _arrivals = itertools.count() # arrival numbers for keep_k, shared by every call |
| 9 | |
| 10 | |
| 11 | def keep_k(items: Iterable[Item], k: int, key: Callable[[Item], float], min_heap: Optional[List[Tuple[float, int, Item]]] = None) -> List[Item]: |
| 12 | """Top-K Heap: the k items with the largest key(item), in any order. |
| 13 | The heap is a min-heap, so its root is the weakest item kept and the first one to evict. |
| 14 | For a stream (Kth Largest Element in a Stream), pass the same min_heap list on every call with the new items. |
| 15 | Then min_heap[0][2] is the k-th largest item seen so far. |
| 16 | Precondition: k >= 1. For the k smallest keys, pass the negated key. |
| 17 | Maximum Subsequence Score needs the sum of the kept keys at each step, so it keeps its own running sum. |
| 18 | N = the items. T = the cost of one key call. |
| 19 | Time: O(N * (T + log K)) | Space: O(K) heap |
| 20 | """ |
| 21 | if min_heap is None: |
| 22 | min_heap = [] # Min-heap of (key, arrival, item); the root is the weakest item kept |
| 23 | |
| 24 | for item in items: |
| 25 | # 1. Push Every Candidate: |
| 26 | # Each entry is (key, arrival, item); arrival is a number never used before, so two items with an equal key are never compared. |
| 27 | heapq.heappush(min_heap, (key(item), next(_arrivals), item)) |
| 28 | |
| 29 | # 2. Evict the Weakest When Over Capacity: |
| 30 | # A popped item is no bigger than k others already seen, so it can never belong in the final top k. |
| 31 | if len(min_heap) > k: |
| 32 | heapq.heappop(min_heap) |
| 33 | |
| 34 | # 3. Return the Kept Items: |
| 35 | # Every item still in min_heap is one of the k biggest keys seen, in no particular order. |
| 36 | return [item for _, _, item in min_heap] |
min_heap holds the k biggest counts seen so far (fewer at the start), and its root is the smallest of them.To keep the k biggest items, keep a min-heap of size k: push each (count, value) pair, and when min_heap grows past k, pop its root, the weakest item kept. A popped item is no bigger than k others already seen, so it can never be in the top k, and after the last push min_heap holds exactly the k biggest, with the k-th biggest at the root. The trap is the heap direction: the k largest need a min-heap and the k smallest a max-heap, because the root must be the item you evict next.
See it run
Top-K Heap, step by step
len(min_heap) 0root none
The top row is min_heap in its array layout, with the index above each cell. The bottom row lists the evicted entries in order. Each entry is (count, value).
rootthe weakest entry kept, first to evictpushedthe entry added at this steppushed, rootthe entry added at this step, now at the rootevictedan entry popped from min_heapkeptan entry in the final answer
Start: counts = {1: 3, 2: 1, 3: 5, 4: 2, 5: 4, 6: 1} and k = 3. min_heap is empty.
Gotcha 1 of 3
- The trap
Gotcha 1: Keeping the
klargest in a max-heapMistakeheapq.heappush(min_heap, (-count, value))Fixheapq.heappush(min_heap, (count, value))What happens: Python's
heapqis a min-heap only. Pushing-countturns it into a max-heap, so the root is the most frequent item. When the heap grows pastk,heappopthen removes the most frequent item. The heap ends with thekleast frequent values. To keep theklargest, the root must be the smallest item kept, so use a min-heap.Example: Top K Frequent Elements (LC 347): nums = [1, 1, 1, 2, 2, 3], k = 2Mistake: returns [2, 3]Fix: returns [1, 2]
- All numbers are given at once, you need one k-th value and no stream follows, as in Kth Largest Element in an Array (LC 215). See Quickselect
- Every item must come out ordered by its count, and counts can't exceed the input length, as in Sort Characters By Frequency (LC 451), not just the top
k. See Bucket Sort
Deep Dive
Why evicting the root is always safe
min_heap is a priority queue: it lets you read and remove its smallest entry quickly. Each entry is (count, value), so the smallest entry is the value with the smallest count. That entry sits at the root.
Take Top K Frequent Elements (LC 347) with nums = [1, 1, 1, 2, 3, 3, 3, 3, 3, 4, 4, 5, 5, 5, 5, 6] and k = 3. The counts are 1: 3, 2: 1, 3: 5, 4: 2, 5: 4 and 6: 1. The code pushes the six pairs in that order.
After the first three pushes, min_heap holds (3, 1), (1, 2) and (5, 3), and the root is (1, 2). Pushing (2, 4) makes four entries, which is more than k. The code pops the root (1, 2). Three entries remain, and they all have a bigger count than the entry that left.
Why this is correct: an evicted entry has a count no bigger than the k entries that stay in min_heap. So at least k values beat it, and it can never be one of the k most frequent. Later pushes only add more competitors, so the decision never needs to be undone.
The end of the example shows a useful case. The pair (1, 6) is pushed last, and it becomes the root at once. The next line pops it, so a new entry can be evicted in the same step it arrives. The final min_heap holds (3, 1), (4, 5) and (5, 3), so the answer is the values [1, 5, 3] in any order.
What a binary heap is
A binary heap is a list that is read as a tree. The entry at index i has its children at indexes 2 * i + 1 and 2 * i + 2, and its parent at index (i - 1) // 2. In a min-heap, every entry is no bigger than its two children. So the smallest entry is always at index 0, the root.
heappush adds the new entry at the end of the list. Then it swaps the entry with its parent while the entry is smaller than the parent. This is called sifting up, and it takes at most log2(len(min_heap)) swaps. heappop removes index 0, moves the last entry to index 0, and sifts it down. At each level, it swaps with the smaller child until the entry is no bigger than both children. This takes at most log2(len(min_heap)) levels too.
Example: push 5, 3, 8, 1, 2, 9, 4 one at a time into an empty list with heapq.heappush. The list becomes [1, 2, 4, 5, 3, 9, 8]. Index 1 holds 2, and its children at indexes 3 and 4 hold 5 and 3. Both are bigger, so the rule holds. heapq.heappop then returns 1, and the list becomes [2, 3, 4, 5, 8, 9].
heapq.heapify(items) turns a list that already exists into a heap in O(n) time, and it does not need n pushes. It sifts down every entry that has children, starting from index n // 2 - 1 and moving back to index 0. Half of the entries are leaves and need no work. Only one entry sits at the top level, and it may move down all the levels. The sum of all the moves is at most about n. In a test, heapify on 1000 distinct random numbers made 729 swaps, and on 100000 numbers it made 74427 swaps. Pushing 1000 numbers in descending order made 7987 swaps, because each new entry sifts all the way up.
Python's heapq is a min-heap only. For a max-heap, push the negated value and negate it again after each pop. Pushing -5, -3, -8, -1, -2, -9, -4 and popping three times gives 9, 8, 5 after negating. This card needs the min-heap itself: the root must be the entry to evict next.
Three ways to find the top K, and their cost
There are three common ways to take the k biggest of n items. Sorting costs O(n log n). The Top-K Heap costs O(n log k), because each of the n pushes and pops works on a heap of at most k + 1 entries. Counting into buckets costs O(n) when the key is a count that can never be bigger than n.
A comparison count shows the difference. With n = 100000 distinct random numbers and k = 10, Python's sorted made about 1.53 million comparisons, and the push-then-pop loop of this card made about 0.70 million. The loop wins because a heap of 11 entries is only about 3 levels deep.
The same test with k = 1000 gives a different result. The heap loop made about 1.85 million comparisons, which is more than the sort. Big-O hides the constant: each pop compares up to two children per level, so the loop costs about 2 * log2(k) comparisons for a pop. Use the heap when k is much smaller than n, or when the data arrives as a stream and cannot be sorted at once.
For Top K Frequent Elements (LC 347), the key is a count, and a count is at most len(nums). So a list of buckets works: buckets[c] holds the values with count c. Then read the buckets from the largest count down until k values are collected. The code is counts = Counter(nums), then buckets = [[] for _ in range(len(nums) + 1)], then for value, count in counts.items(): buckets[count].append(value). It makes no comparison between two counts, and its time is O(n). On 3000 random inputs, the bucket version, a sort version and the heap version returned the same values.
The buckets need a small, known range of keys. They do not work for the k closest points or the k biggest sums, where the key can be any number. The heap works for any key you can compare, so it is the general choice. The tool card Quickselect finds one k-th value in O(n) on average when every value is in memory.
The stream form, and equal keys
In Kth Largest Element in a Stream (LC 703), numbers arrive one at a time, and the k-th largest is asked after each one. The template keeps min_heap between calls: pass the same list each time. After each push and pop, min_heap[0] is the k-th largest number seen so far. With k = 3 and the numbers 4, 5, 8, 2, the root after the fourth number is 4.
The general keep_k pushes (key(item), next(_arrivals), item). The middle number is an arrival counter. When two items have the same key, Python compares the arrival numbers next, and they are never equal. So Python never has to compare the items themselves. This matters when the items are objects or lists that cannot be compared with <.
The k smallest keys need a max-heap, which in Python means negating the key: key=lambda item: -item. The root is then the biggest of the kept keys, which is the one to evict.
60-Second Pitch
I need the k most frequent values, and k is small. I count the values first, which takes O(n).
Then I keep a min-heap of size k. I push each (count, value) pair, and when the heap holds more than k, I pop the root. The root has the smallest count, so I drop the weakest entry. An entry I drop already has k entries at least as frequent, so it can never be in the answer.
The time is O(n log k), and the space is O(n) for the counts plus O(k) for the heap.
If the counts are at most n, buckets give O(n) with no heap. If the data is a stream, I keep the same heap between calls, and the root is the k-th largest.
A max-heap would be the wrong choice, because it pops the item I want to keep.
Common Questions
- Why a min-heap to keep the largest items?
The item to evict next is the weakest one kept. A min-heap puts the smallest key at the root, so the weakest item is the one
heappopremoves. A max-heap would remove the strongest.- What is the time and space, and why is it not O(n log n)?
Each of the
nitems costs one push and at most one pop. The heap never holds more thank + 1entries, so each operation costs O(log k). The total is O(n log k) time andO(k)space for the heap. With the counts, the space isO(n).- When would you not use a heap?
When
kis close ton, sorting is as cheap and simpler. When the keys are counts that can never exceedn, buckets giveO(n). When you need one k-th value and every value is in memory, Quickselect isO(n)on average.- How do you handle a stream?
Keep
min_heapbetween calls, and push each new number. If the heap holds more thankentries, pop the root. After that,min_heap[0]is the k-th largest number seen so far. This is Kth Largest Element in a Stream (LC 703).- How do you get the k smallest, or break ties?
For the
ksmallest, push the negated key, so the root is the biggest kept key. For ties, push(key, arrival, item)with an arrival number that never repeats, so Python never compares the items.
SWE Reasoning
Spot it
- The question asks for the
klargest, most frequent, closest or best items, andkis much smaller thann. - The data arrives as a stream, and the answer must be ready after every new item.
- Sorting everything would work, but it keeps more than the answer needs.
- The answer can be given in any order.
- Cheapest-first search on a weighted graph uses the same tool: a heap that always gives the smallest entry. Read the Dijkstra's Algorithm card in the Graphs pattern for it.
The invariant, step by step
min_heapstarts empty. It will hold at mostkentries after every step.- Each candidate is pushed as
(count, value), so the heap orders entries by count first. - When
min_heapholds more thankentries, the root is popped. The root has the smallest count, so it is the weakest entry kept. - The popped entry has a count no bigger than
kothers already seen, so it can never be in the topk. - After the last push and pop,
min_heapholds exactly thekbiggest counts, and its root is the k-th biggest.
Traps
- A max-heap (pushing
-count) pops the most frequent entry. The heap ends with thekleast frequent values. For theklargest, the root must be the smallest kept entry. - Pushing
(value, count)orders the heap by value. The code then keeps the biggest values and returns the wrong ones. - Evicting at
len(min_heap) >= kkeeps onlyk - 1entries. The test islen(min_heap) > k. - Equal keys on objects that cannot be compared raise an error when the tuple reaches its last item. An arrival number before the item prevents it.
- Using the heap when
kis close toncosts more than sorting.