Count everything, then rank by two rules at once: most frequent first, ties broken alphabetically. The real algorithm lives in the comparator.
Given a list of words and a number K, return the K most frequent — with a tie-break:
when two words appear equally often, the alphabetically smaller one ranks higher. So
apple banana apple banana cat with K=3 gives apple, banana, cat (apple and banana tie
at 2, apple wins the alphabet).
The input repeats words, so stage one is a HashMap<String,Integer> built in one O(N) pass —
the same counting move as the earlier frequency problems. What's new is stage two: once you have
word → count, you must rank the unique words by the two-key rule and slice off the top K.
Don't treat the comparator as implementation trivia — it encodes the definition of "better." Compare two words:
if their counts differ, the higher count wins; if they tie, the alphabetically smaller wins. In Java, since
sort is ascending by comparator result, you reverse the frequency comparison but leave the word
comparison natural.
uniqueWords.sort((a, b) -> {
int fa = freq.get(a), fb = freq.get(b);
if (fa != fb) return Integer.compare(fb, fa); // freq DESC
return a.compareTo(b); // word ASC
});
compare(fb, fa) (reversed → descending). The tie-break uses
a.compareTo(b) (natural → ascending). Flip either and the ranking is wrong.
Score ranks players; when two are level, a fixed rule (say, alphabetical name) breaks the tie. Frequency is the score; the alphabet is the tiebreaker.
Sort by "count" descending, then by "name" ascending. Multi-key sorting is exactly this — the comparator just chains the columns.
A heap of size K keeps only the top K seen so far — the weakest is at the door, evicted the moment someone better arrives. Handy when K ≪ total.
Type words (space-separated) and dial K. Every unique word is counted and ranked by the two-key
rule; bars show frequency. The top K sit above the dashed cutoff in teal; everything below is dropped. Watch ties resolve
alphabetically.
Try dog cat bat dog cat bat (all tie → pure
alphabetical) or z a z c b (one dominant word, then a three-way tie).
For apple banana apple orange banana apple mango orange, K=3:
| Rank | Word | Frequency | Why here |
|---|---|---|---|
| 1 | apple |
3 | highest count |
| 2 | banana |
2 | tie at 2, banana < orange |
| 3 | orange |
2 | tie at 2, loses alphabet → but still in top 3 |
| — | mango |
1 | below cutoff |
import java.util.*; static List<String> topKFrequent(String[] words, int k) { Map<String,Integer> freq = new HashMap<>(); for (String w : words) freq.put(w, freq.getOrDefault(w, 0) + 1); List<String> uniq = new ArrayList<>(freq.keySet()); uniq.sort((a, b) -> { int fa = freq.get(a), fb = freq.get(b); return fa != fb ? Integer.compare(fb, fa) : a.compareTo(b); }); return new ArrayList<>(uniq.subList(0, k)); }
Counting is O(N); sorting the U unique words is O(U log U); total O(N + U log U), O(U) space.
With N ≤ 1000 that's trivially fast — the simplest approach that comfortably fits the constraints is the right
one. Java's declarative form works too:
Comparator.comparingInt(freq::get).reversed().thenComparing(naturalOrder()).
If unique words number in the millions but K is tiny, sorting everything is wasteful. Instead keep a size-K min-heap whose root is the worst currently-selected word. Each new word is offered; if the heap exceeds K, poll the root. That's O(U log K) — better when K ≪ U.
b.compareTo(a)).
After polling, reverse the collected list to get final order.
| Approach | Time | Best when |
|---|---|---|
| Sort all unique | O(N + U log U) | simpler; fine for small N |
| Size-K min-heap | O(N + U log K) | K ≪ U (huge candidate set) |
Sorts words alphabetically, but frequency is the primary key. zebra:10 must beat
apple:1. Key-order alone can't express that.
Tells you which words exist, not how often. Ranking is frequency-first, so you need counts — a map, not a set.
No order guarantee at all — not by frequency, not alphabetical. You must explicitly sort or use a configured heap.
Ignores frequency entirely. bus car car train → the answer is car, bus, not
bus, car.
This is the fourth question in the set built on a frequency map — and it shows the split cleanly. Stage one is always "what do I need to know about each distinct value?" Here: its count. Stage two is dictated by the exact wording: