Top K Frequent Words
Count every word, then keep a min-heap of the k strongest words, where a tie in count goes to the alphabetically earlier word.
Watch it animatedStep through it, with the running line highlighted. Free with an account.The idea
First count each word with a dictionary. To keep the k best words, use a min-heap of size k whose top is the weakest word kept: the lowest count, and on equal counts the word that comes later in the alphabet. Each new word is pushed; if the heap then holds k + 1 words, pop the top, the weakest of them. What survives is the top k. Popping gives them weakest first, so reverse the list at the end.
Code
public IList<string> TopKFrequent(string[] words, int k) {
var count = new Dictionary<string, int>();
foreach (var w in words) count[w] = count.GetValueOrDefault(w) + 1;
// weakest on top: the lower count, or on a tie the alphabetically later word
var heap = new PriorityQueue<string, (int c, string w)>(Comparer<(int c, string w)>.Create(
(x, y) => x.c != y.c ? x.c.CompareTo(y.c) : string.CompareOrdinal(y.w, x.w)));
foreach (var (w, c) in count) {
heap.Enqueue(w, (c, w));
if (heap.Count > k) heap.Dequeue(); // drop the weakest of k + 1
}
var result = new List<string>();
while (heap.Count > 0) result.Add(heap.Dequeue()); // weakest first
result.Reverse();
return result;
}Tips
- The tie rule is flipped inside the heap: the answer wants "day" before "sunny", so in the min-heap "sunny" counts as weaker and sits nearer the top.
- Sorting all distinct words by (count desc, word asc) and taking k also works, in O(m log m) for m distinct words. The heap wins when k is much smaller than m.
- string.CompareOrdinal compares char codes, which is alphabetical order for lowercase words.
In the .NET library
PriorityQueue<TElement, TPriority> (.NET 6+) is a min-heap. A value-tuple priority with a custom Comparer orders by count first and then by word, all in one comparison.