Top K Frequent Words
Problem statement
You get a list of words and an integer k. Return the k words that appear most often, ordered from most frequent to least frequent. When two words appear the same number of times, the one that comes first alphabetically goes first.
k is at most the number of distinct words.
Examples
Example 1
Input: words = ["deploy", "build", "deploy", "test", "build", "deploy"], k = 2
Output: ["deploy", "build"]
Explanation: deploy appears 3 times and build twice.
Example 2
Input: words = ["web", "db", "cache", "db", "web", "api"], k = 3
Output: ["db", "web", "api"]
Explanation: db and web both appear twice, so db comes first alphabetically. api and cache tie on one each, and api wins the last spot.
Hints
Approach
Count, then keep only the best k in a min-heap.
- Count occurrences with a hash map.
- Define "weaker": a lower count is weaker; with equal counts, the word later in the alphabet is weaker. The heap's root is always the weakest word it holds.
- Push each distinct word. If the heap holds more than
k, pop the root. - Pop everything left. It comes out weakest first, so reverse it to get the required order.
The tie-break direction is the subtle part: inside the heap the alphabet is reversed, because the word you want to throw away on a tie is the one later in the alphabet. In Python a small class with __lt__ expresses this cleanly, since negating a string is not possible.
O(n + m log k)Space O(m)import heapqfrom collections import Counter class Entry: """Heap item where the 'smallest' entry is the weakest candidate.""" def __init__(self, count: int, word: str): self.count = count self.word = word def __lt__(self, other: "Entry") -> bool: if self.count != other.count: return self.count < other.count # fewer occurrences = weaker return self.word > other.word # same count: later in the alphabet = weaker class Solution: def topKFrequent(self, words: list[str], k: int) -> list[str]: counts = Counter(words) heap = [] # min-heap of the k strongest words for word, count in counts.items(): heapq.heappush(heap, Entry(count, word)) if len(heap) > k: heapq.heappop(heap) # evict the weakest result = [] while heap: result.append(heapq.heappop(heap).word) return result[::-1] # popped weakest-first, so reverseFollow-up questions
- The log is too large for memory. How would you find the top
kwords across many files or machines? - Return each word with its count, formatted as a report.
Frequently asked questions
That problem lets you return the answer in any order and has no ties to break. Here the output order matters and ties must be broken alphabetically, which is what makes the heap comparator tricky.
The root of a min-heap is the item that gets evicted. On a count tie you want to keep db and evict web, so web must count as smaller, which means comparing words in reverse. The final reverse then restores the normal order.
Reporting the top error messages, the busiest endpoints, or the most frequent user agents from a log is exactly this, and a stable, alphabetical tie-break keeps reports from changing order between runs when counts are equal.