Your country

Tools that support it use your country for local currency, number formats, units and paper size. Your choice is saved only in this browser.

Type a name or a two-letter code. Use the up and down arrow keys to move through the countries, Enter to choose one and Escape to close.

Coding Interview Patterns  Module 13 – Heaps and top-K

Top k frequent words: heap or buckets

The k most searched words of a log, ties in alphabetical order: count them, then pick the top k with a size-k heap or with buckets indexed by count.

  • Intermediate
  • 30 minutes
  • Examples run with Python 3.14.8, Pyodide 314.0.7, Node.js 24.21.0, quickjs 0.32.0, Java 25, Clang 21.0.0 and Go 1.27.1
  • By MySmartCoPilot
Free intro, Pro: 6 of 30 minutes free
Free: The problem: the most searched station codes · The brute force: one scan of the log per code · What to notice. With a pass: the other 13 sections and 3 interview answers, about 24 minutes.

What you will learn

  • Find the k most frequent words of a log with a hash map and a heap of size k
  • Find the k most frequent words with buckets indexed by count, and say when the buckets cost too much
  • Apply an alphabetical tie rule with one order shared by the heap and the answer, in five languages
  • Measure counting and the ways of picking the top k on logs of a million searches
  • Reproduce three tie-breaking slips and catch them with a brute-force judge

Before you start

What you’ll learn · Before you start

What you will learn

  • Find the k most frequent words of a log with a hash map and a heap of size k
  • Find the k most frequent words with buckets indexed by count, and say when the buckets cost too much
  • Apply an alphabetical tie rule with one order shared by the heap and the answer, in five languages
  • Measure counting and the ways of picking the top k on logs of a million searches
  • Reproduce three tie-breaking slips and catch them with a brute-force judge

Before you start

On this page

Top k frequent words asks for the k words that occur most often in a list, most frequent first, with a rule for words that occur equally often. It takes two steps: count every word with a hash map in one pass, then pick the k best (word, count) pairs, the top-k pattern of the previous lesson applied to counts. The tie rule is where answers go wrong. When equal counts are broken alphabetically, the order compares counts first and words second, and the picking step must follow exactly that order.

The problem: the most searched station codes

The problem

A railway enquiry app logs every station code that users search for, such as NDLS or SBC. Given the log log and a number k, return the k most searched codes, most searched first; codes searched equally often appear in alphabetical order. Limits: up to 10⁶ searches, each code 2 to 5 letters from A to Z, at most 10⁴ distinct codes, and 1 ≤ k ≤ the number of distinct codes.

  1. NDLS, SBC, NDLS, HWH, SBC, NDLS, MAS with k = 2 gives [NDLS, SBC]: three searches, then two.
  2. SBC, MAS, HWH, MAS, SBC, HWH with k = 2 gives [HWH, MAS]: every code was searched twice, so alphabetical order alone decides, and SBC is left out.
  3. BCT with k = 1 gives [BCT].
Two logs side by side, each counted and then ordered by count and alphabet: the first gives NDLS, SBC and the second HWH, MAS.Log 1NDLS SBC NDLS HWHSBC NDLS MASLog 2SBC MAS HWHMAS SBC HWHCountsNDLS 3 · SBC 2HWH 1 · MAS 1CountsHWH 2 · MAS 2SBC 2Higher count first,then alphabeticalAll counts equal:alphabetical decidesk = 2:NDLS, SBCk = 2:HWH, MAScountcount

Counting comes first; the order rule then picks the k codes

Text description of the diagram

The figure shows the first two examples side by side, each as four boxes from top to bottom.

Left column, log 1:

  1. The log: NDLS, SBC, NDLS, HWH, SBC, NDLS, MAS.
  2. Counting gives NDLS 3, SBC 2, HWH 1 and MAS 1.
  3. The rule: higher count first, then alphabetical order.
  4. The answer for k = 2: NDLS, then SBC.

Right column, log 2:

  1. The log: SBC, MAS, HWH, MAS, SBC, HWH.
  2. Counting gives HWH 2, MAS 2 and SBC 2.
  3. All counts are equal, so alphabetical order alone decides.
  4. The answer for k = 2: HWH, then MAS. SBC is left out although it was searched as often, because it comes last alphabetically.

The brute force: one scan of the log per code

For each distinct code, scan the whole log and count it. Then sort the codes by count, highest first and alphabetically among equal counts, and return the first k. It is plainly right, so it can judge faster code, but its scans cost O(n · d) for n searches and d distinct codes. At 10⁶ searches and 10⁴ codes that is 10¹⁰ log entries read, about 100 seconds of C++ at 10⁸ simple steps a second.

One scan per code, and what it would read on a larger log Python · brute/scan_each.py
# The brute force for the k most searched station codes: count each distinct code by scanning the whole log, then
# sort the distinct codes by count, highest first, and alphabetically among equal counts. The last line works out how
# many log entries those scans would read on a synthetic log of 200,000 searches over about 2,000 codes.
import random
import string


def most_searched_by_scanning(log, k):
    counts = []
    for code in sorted(set(log)):
        seen = 0
        for entry in log:  # one full scan of the log per distinct code
            if entry == code:
                seen += 1
        counts.append((code, seen))
    counts.sort(key=lambda pair: (-pair[1], pair[0]))  # higher count first, then alphabetical
    return [code for code, _ in counts[:k]]


print(most_searched_by_scanning(["NDLS", "SBC", "NDLS", "HWH", "SBC", "NDLS", "MAS"], 2))
print(most_searched_by_scanning(["SBC", "MAS", "HWH", "MAS", "SBC", "HWH"], 2))
print(most_searched_by_scanning(["BCT"], 1))

rng = random.Random(9)
codes = ["".join(rng.choices(string.ascii_uppercase, k=4)) for _ in range(2_000)]
log = [rng.choice(codes) for _ in range(200_000)]
distinct = len(set(log))
print(f"{len(log):,} searches of {distinct:,} codes: one scan per code would read {len(log) * distinct:,} log entries")

Output

['NDLS', 'SBC']
['HWH', 'MAS']
['BCT']
200,000 searches of 1,996 codes: one scan per code would read 399,200,000 log entries

Recorded with Python 3.14.8 on macOS 26 arm64. To run it yourself: mise exec python@3.14.8 -- python3 scan_each.py

On the synthetic log of 200,000 searches, one scan per code would read 399,200,000 entries, about 2,000 times the length of the log itself.

What to notice

The scans count each code separately, and one pass with a hash map from code to count does it for all of them, as in the lesson on frequency maps. After counting, the question is a top-k over d (code, count) pairs, ordered by count first and then alphabetically. Three ways pick those pairs:

  • Count and sort sorts all d pairs by that order and keeps the first k, in O(d log d) time.
  • A size-k heap keeps the k best pairs, with the worst of them on top, in O(d log k) time.
  • Buckets by count list the codes of each count in an array indexed by count, then walk it from the top.

Counting in one pass, then a top-k over the pairs Pro: opens with a Pro, Premium or Ultimate pass

In this part:

  • The size-k heap, with the worst pair on top
    • Why the k best pairs survive, proved with an invariant
    • A dry run on the tie example, k = 2
  • The code in five languages
  • Buckets indexed by count
  • Checked against a judge on 3,000 logs
  • Measured: counting, sorting, the heap and the buckets on a million searches
  • Three tie slips, reproduced
  • Edge cases and common bugs
  • Exercise: the k most frequent words
    • The k most frequent words, ties in alphabetical order
  • Variants: numbers, sparse buckets, per region and the last hour
  • A practice set with sample answers
  • Check yourself
  • Interview questions
    • In the k most frequent words problem with ties broken alphabetically, why does the order need two keys, and which comes first?
    • You need the 100 most frequent words of 10⁸ searches with about 10⁶ distinct words, ties broken alphabetically. Compare counting and sorting, a size-k heap and buckets by count. Which do you choose, and what would change your mind?
    • A search box shows the 10 most searched words of the last hour, refreshed every second, at about 2,000 searches a second. Design the data structures, and give the cost of handling one search and of one refresh.
    • A solution returns [w for w, _ in Counter(words).most_common(k)] and fails a hidden test of the alphabetical tie rule. Explain the failure, give the smallest input that shows it, and fix it with as few changes as possible.
  • Key takeaways

7 runnable examples (C++, Go, Java, JavaScript, Python) · 1 exercise with sample tests · 3 interview answers · about 25 minutes

Opening this part with your pass…

This part opens with any MySmartCoPilot pass.

  • Pro pass · 1 week₹79$2.99
  • 30 days₹179$5.99
  • 1 year₹1,699$45

One payment; a pass never renews. The price shown is the total you pay: no fees or charges are added at checkout.

It also opens every other in-depth part in Learn and every Pro tool. Sales are final, except where the law gives you a right to withdraw, for example in the EU and the UK (refund policy).

References

Related tools

Report a problem with this lesson

Quick answers and tool search

Type to search tools or to get a quick answer, for example 18% of 2500. Use the up and down arrow keys to move through the results, Enter to choose, and Escape to close.