Heaps · Heap Fundamentals

Heap Frequency Ranking

Counting occurrences first, then ranking the distinct values by count - a heap (or a direct sort) turns "the k most common values" into a selection problem instead of a full sort of every distinct value.

  • 4 min read
  • 7 practice questions
  • 1 graded problem
On this page
  1. Description
  2. The approach
  3. A second flavor: bucket sort by count
  4. Where you'll see it
  5. Which tool for which job?
  6. Common mistakes
  7. Check yourself
  8. Key takeaways
  9. Quick revise
  10. Practice

Description

"The 3 most common error codes today", "the 10 most-viewed pages": these rank distinct values by how often they occur. The raw data may have millions of entries but only thousands of distinct values - so count first, then select among the distinct values.

This skill covers count-then-select: a hash map of counts, then either a size-k heap or buckets indexed by count. It also covers writing the tie rule into the comparator and reading results in the right order. Approximate counting appears briefly, for when the distinct values themselves don't fit in memory.

We'll go from simple to more technical:

  1. The pattern: count with a map, then keep the k strongest in a min-heap with a tie rule.
  2. A second flavor: bucket sort by count, which needs no heap and runs in O(n).
  3. Where frequency ranking shows up (top pages, error codes, trending tags).
  4. What breaks it: no tie rule, the reversed tie, output order, selecting on raw data.

The approach

Java

int[] topKFrequent(int[] nums, int k) {
    Map<Integer, Integer> count = new HashMap<>();
    for (int x : nums) count.merge(x, 1, Integer::sum);          // O(n)
    // "weaker" = lower count, or the same count and a LARGER value (smaller values must win ties)
    PriorityQueue<Integer> heap = new PriorityQueue<>((a, b) ->
        !count.get(a).equals(count.get(b)) ? Integer.compare(count.get(a), count.get(b))
                                           : Integer.compare(b, a));
    for (int v : count.keySet()) {
        heap.offer(v);
        if (heap.size() > k) heap.poll();                        // evict the weakest
    }
    int[] result = new int[k];
    for (int i = k - 1; i >= 0; i--) result[i] = heap.poll();    // the heap pops weakest first
    return result;
}

Python

import heapq
from collections import Counter


def top_k_frequent(nums: list[int], k: int) -> list[int]:
    count = Counter(nums)                                    # O(n)
    heap = []                                                # a tuple is the comparator:
    for v, c in count.items():                               # (count, -v) - lower count, or the same count
        heapq.heappush(heap, (c, -v))                        # and a LARGER value, is the weaker one
        if len(heap) > k:
            heapq.heappop(heap)                              # evict the weakest
    result = [-heapq.heappop(heap)[1] for _ in range(len(heap))]
    return result[::-1]                                      # the heap pops weakest first

After counting, the heap runs over the d distinct values: O(n + d log k). Two details decide correctness. Ties need a rule, and it belongs in the comparator: if smaller values win ties, then among equal counts the larger value is the weaker one to evict. And a min-heap pops from weakest to strongest, so the result is filled from the back. In Python a tuple takes the comparator's place - (count, -value) sorts weaker entries first - and Counter.most_common(k) is no shortcut here: it breaks ties by first appearance, not by value.

A second flavor: bucket sort by count

Counts range from 1 to n, so values can be bucketed by count and read from the highest bucket down - no heap, O(n) in total:

Java

List<Integer>[] buckets = new List[nums.length + 1];         // buckets[c] = values seen c times
for (var e : count.entrySet()) {
    int c = e.getValue();
    if (buckets[c] == null) buckets[c] = new ArrayList<>();
    buckets[c].add(e.getKey());
}
// walk c from n down to 1, sort each bucket for the tie rule, and take values until you have k

Python

buckets = [[] for _ in range(len(nums) + 1)]                 # buckets[c] = values seen c times
for value, c in count.items():
    buckets[c].append(value)
# walk c from n down to 1, sort each bucket for the tie rule, and take values until you have k

Where you'll see it

Top pages, top error codes, trending hashtags and "most frequent queries" reports are count-then-select. At very large scale the exact count map is replaced by approximate structures (Count-Min sketch plus a heap of candidates, or Space-Saving) that keep memory bounded while tracking the heavy hitters.

Which tool for which job?

You needUse
Top k by count, k smallcount, then a size-k min-heap
Top k by count in O(n)count, then buckets indexed by count
Few distinct valuescount, then sort the distinct values
Heavy hitters in a stream too big to count exactlySpace-Saving or Count-Min with a heap
Top k across many serversmerge full counts (or enough candidates), not each server's top k

Common mistakes

MistakeWhat happensDo this instead
No tie ruleequal counts come out in hash orderbreak ties on the value in the comparator
The tie reversed in a min-heapthe value that should win is evictedthe weaker side is the larger value when smaller wins
Reading the heap as the answerweakest firstfill the result from the back
most_common(k) with a value tie ruleties come out by first appearancesort by (-count, value)
Heaping the raw arrayranks by value, needless workcount first

Check yourself

Try to answer before opening each one - recalling beats re-reading.

1. Why count first and then select?

Show answer

The question ranks distinct values by frequency. Counting turns it into "the k largest counts" over d distinct values, which is much smaller than the n raw entries - O(n + d log k) instead of working on every repeat.

2. Smaller values win ties. Which value should a size-k min-heap evict among two with equal counts?

Show answer

The larger one - it's the weaker of the two under the tie rule, so the comparator orders equal counts by value descending (Integer.compare(b, a)).

3. How does bucket sort make top-k frequency O(n)?

Show answer

Counts are integers from 1 to n, so each distinct value goes into bucket[count]. Reading buckets from n down to 1 yields values by decreasing frequency without any comparison sort.

Key takeaways

  • Count with a map, then select among distinct values.
  • A size-k min-heap: O(n + d log k); pop order is weakest first, so reverse.
  • Put the tie rule in the comparator - and remember the heap evicts the weaker side.
  • Bucket sort by count gives O(n).

Quick revise

The whole skill on one page, for the night before an interview.

This page puts the lesson and the deep dive together. Skim it before practice or an interview, and go back to the full lesson for anything you don't recognize.

Pick the tool

You needUse
Top k by countcount, size-k min-heap
O(n)count, buckets by count
Few distinct valuescount, sort
Stream too big to countSpace-Saving, or Count-Min plus a heap
Many serversmerge counts or sketches

Snippets worth knowing by heart

Java

Map<Integer, Integer> count = new HashMap<>();
for (int x : nums) count.merge(x, 1, Integer::sum);
PriorityQueue<Integer> heap = new PriorityQueue<>(            // weakest on top
    Comparator.<Integer>comparingInt(count::get).thenComparing(Comparator.reverseOrder()));

Python

from collections import Counter
count = Counter(nums)
top = sorted(count, key=lambda v: (-count[v], v))[:k]   # smaller value wins ties

The traps

  • No tie rule, or a reversed one: equal counts come out in hash order, or the winner is evicted. Tie-break in the comparator.
  • Weakest-first output: the min-heap pops the weakest first. Fill the result from the back.
  • most_common ties by first appearance, not by value. Sort by (-count, value) when values break ties.
  • Merging local top-k lists: a value just under the cut everywhere can win overall. Merge full counts, then pick k.

Staff-level points, one line each

  • A TreeSet ordered by count alone kept 2 of 4 keys; adding "then by name" kept 4.
  • Space-Saving, 100 counters over 200,000 values: every value above n/m kept, overestimates at most 1,488 (bound 2,000), exact top 10.
  • Count-Min never underestimates; 4 x 2,000 kept every value within 200.
  • Per-shard top 2 named a 10 as the leader; the true top was "x" with 24.

Say it in an interview

  • "Count first, then select among the distinct values."
  • "The tie rule goes in the comparator, so the order is total."
  • "If the distinct values don't fit, I'd use Space-Saving; it keeps every value above n/m."
  • "Across shards I'd merge counts or sketches, not each shard's top k."

Practice

This skill comes with 7 multiple-choice questions that tell you which misconception a wrong answer reveals and 1 graded problem. Your answers are graded and your mastery is tracked, so you'll know when the skill holds up. Running code and saving progress need a free account.

Practice Heap Frequency Ranking free

Top K Frequent Elements

MediumProCoding problemJava · Python

Return the k values that appear most often in nums, most frequent first. When two values appear equally often, the smaller value comes first.

The full statement, the examples and the hidden test suite are part of AI Proof Engineers Pro.

Practice Heap Frequency Ranking free

No card needed. Lessons, practice questions and easy problems are free forever.

Free plan, no card · 7-day Pro trial · Cancel anytime