LC 347 ยท Top K Frequent Elements โ Solution & Explanation | DSA Guide
Three approaches to LeetCode Top K Frequent Elements โ sorting O(n log n), min-heap O(n log k), and optimal bucket sort O(n). Full Java and Python solutions with visual walkthrough.
LeetCode ยท #347
Top K Frequent Elements Solution
Given an integer array nums and an integer k, return the k most frequent elements. The answer may be returned in any order.
Given an integer array nums and an integer k, return the k most frequent elements. You may return the answer in any order. It is guaranteed that the answer is unique.
Example
Input: nums = [1, 1, 1, 2, 2, 3], k = 2Output: [1, 2]
// 1 appears 3 times, 2 appears 2 times โ these are the top 2
Constraints
โข 1 โค nums.length โค 10โตโข -10โด โค nums[i] โค 10โดโข k is in the range [1, number of unique elements]โข The answer is guaranteed to be unique โ Frequencies are bounded by n โ enables bucket sort
02
Section Two ยท Approach 1
Brute Force โ Sort by Frequency O(n log n)
Count frequencies with a HashMap, then sort the entries by frequency in descending order. Take the first k elements from the sorted list.
Why it works
Sorting by frequency guarantees the most frequent elements come first. Taking the first k gives the correct answer.
Why we can do better
Problem: Sorting all unique elements costs O(m log m) where m is the number of distinct values. We only need the top k โ a min-heap of size k gives O(m log k), and bucket sort exploits the bounded frequency range for O(n).
Java โ Sort by Frequency
import java.util.*;
classSolution {
publicint[] topKFrequent(int[] nums, int k) {
Map<Integer, Integer> freq = newHashMap<>();
for (int n : nums) freq.merge(n, 1, Integer::sum);
return freq.entrySet().stream()
.sorted((a, b) -> b.getValue() - a.getValue())
.limit(k)
.mapToInt(Map.Entry::getKey)
.toArray();
}
}
Python โ Sort by Frequency
from collections importCounterclassSolution:
deftopKFrequent(self, nums: list[int], k: int) -> list[int]:
count = Counter(nums)
return [x for x, _ in count.most_common(k)]
Metric
Value
Time
O(n log n) โ dominated by sorting frequencies
Space
O(n) โ frequency map
03
Section Three ยท Approach 2
Bucket Sort โ O(n)
The maximum possible frequency is n (the array length). Create an array of n+1 buckets where bucket[i] holds all elements with frequency i. Scan buckets from high to low, collecting elements until we have k.
๐ก Mental model: Imagine a building with floors numbered 1 to n. Each floor represents a frequency count. You walk the building placing each unique number on the floor matching how many times it appears. To find the top k, you take the elevator to the top floor and walk down, picking up numbers from each floor until your basket has k items.
Algorithm โ Bucket Sort by frequency
Step 1: Build a frequency map: num โ count.
Step 2: Create List<Integer>[] buckets of size n+1. For each entry in the frequency map, add the number to buckets[count].
Step 3: Iterate from buckets[n] down to buckets[1], collecting elements into the result until result.size() == k.
Step 4: Return the result.
๐ฏ When to recognize this pattern:
Any time the problem asks for the top k or k-th largest/smallest and the metric is bounded (frequencies โค n, values in small range) โ think bucket sort.
If the metric is unbounded, use a min-heap of size k for O(n log k).
This pattern appears in LC 347, LC 451 (Sort Characters by Frequency), and LC 692 (Top K Frequent Words).
Why bucket sort beats heap here:
A min-heap gives O(n log k) โ excellent when k is small.
But bucket sort gives true O(n) because frequencies are naturally bounded by n.
The trade-off is O(n) extra space for the bucket array, but we already need O(n) for the frequency map.
04
Section Four ยท Trace
Visual Walkthrough
Trace through nums = [1, 1, 1, 2, 2, 3], k = 2:
Bucket Sort โ frequency map โ buckets โ top k
Notice:
No sorting needed.
The bucket index is the frequency, so scanning from high to low naturally gives elements in frequency order.
We stop as soon as we have k elements โ no wasted work.
05
Section Five ยท Implementation
Code โ Java & Python
Java โ Bucket Sort
import java.util.*;
classSolution {
publicint[] topKFrequent(int[] nums, int k) {
Map<Integer, Integer> freq = newHashMap<>();
for (int n : nums) freq.merge(n, 1, Integer::sum);
// Bucket index = frequency, value = list of nums with that frequencyList<Integer>[] buckets = newList[nums.length + 1];
for (int i = 0; i < buckets.length; i++) buckets[i] = newArrayList<>();
for (var e : freq.entrySet())
buckets[e.getValue()].add(e.getKey());
int[] result = newint[k];
int idx = 0;
for (int i = buckets.length - 1; i >= 0 && idx < k; i--)
for (int num : buckets[i]) // collect from highest freq downif (idx < k) result[idx++] = num;
return result;
}
}
Python โ Bucket Sort
from collections importCounterclassSolution:
deftopKFrequent(self, nums: list[int], k: int) -> list[int]:
freq = Counter(nums)
buckets = [[] for _ in range(len(nums) + 1)]
for num, count in freq.items():
buckets[count].append(num)
result = []
for i in range(len(buckets) - 1, 0, -1):
result.extend(buckets[i])
if len(result) >= k:
return result[:k]
return result # unreachable
06
Section Six ยท Analysis
Complexity Analysis
Approach
Time
Space
Trade-off
Sort by Frequency
O(n log n)
O(n)
Straightforward; sort overhead for all unique elements
Min-Heap of size k
O(n log k)
O(n + k)
Excellent when k โช n; heap maintains top-k invariant
Bucket Sort โ optimal
O(n)
O(n)
True linear; exploits bounded frequency range
Why not heap?:
A min-heap of size k works well and is the go-to in many similar problems.
But here, frequencies are bounded by n โ bucket sort avoids all log factors.
In an interview, mention both approaches: heap for the general case, bucket sort when the range is bounded.
07
Section Seven ยท Edge Cases
Edge Cases & Pitfalls
Case
Input
Why It Matters
k equals unique count
[1,2,3], k=3
Return all unique elements โ every bucket contributes.
Single element
[1], k=1
Minimum input. Only one unique element, frequency 1.
All same
[5,5,5,5], k=1
One element at frequency n. Bucket[4] = [5].
Negative numbers
[-1,-1,2], k=1
HashMap handles negatives โ frequency counting works the same.
Tied frequencies
[1,1,2,2,3], k=2
Guaranteed unique answer per constraints โ but ties land in same bucket, both collected.
โ Common Mistake: Creating bucket array of size n instead of n+1. An element can appear n times, requiring bucket[n] โ which needs array length n+1. Off-by-one here causes ArrayIndexOutOfBoundsException.