Top K Frequent Elements
Return the k most frequent elements via bucket sort by frequency.
4 min read
top-k heaps bucket-sort
Problem#
Return the k most frequent elements of nums in any order.
Examples#
nums = [1,1,1,2,2,3], k = 2→[1, 2]nums = [1], k = 1→[1]
Constraints#
1 <= nums.length <= 10^5,1 <= k <= |distinct|.
Approach#
Min-heap of size k — O(n log k).
Bucket by frequency — O(n). Frequencies are bounded by
n, so an array indexed by frequency suffices. Solution#
class Solution {public: vector<int> topKFrequent(vector<int>& nums, int k) { unordered_map<int,int> freq; for (int x : nums) ++freq[x]; vector<vector<int>> buckets(nums.size() + 1); for (auto& [v, f] : freq) buckets[f].push_back(v); vector<int> ans; for (int f = buckets.size() - 1; f >= 0 && (int)ans.size() < k; --f) { for (int v : buckets[f]) { ans.push_back(v); if ((int)ans.size() == k) break; } } return ans; }};func topKFrequent(nums []int, k int) []int { freq := map[int]int{} for _, x := range nums { freq[x]++ } buckets := make([][]int, len(nums)+1) for v, f := range freq { buckets[f] = append(buckets[f], v) } ans := make([]int, 0, k) for f := len(buckets) - 1; f >= 0 && len(ans) < k; f-- { for _, v := range buckets[f] { ans = append(ans, v) if len(ans) == k { break } } } return ans}from collections import Counterfrom typing import List
class Solution: def topKFrequent(self, nums: List[int], k: int) -> List[int]: freq = Counter(nums) buckets: List[List[int]] = [[] for _ in range(len(nums) + 1)] for v, f in freq.items(): buckets[f].append(v) ans: List[int] = [] for f in range(len(buckets) - 1, -1, -1): for v in buckets[f]: ans.append(v) if len(ans) == k: return ans return ansvar topKFrequent = function(nums, k) { const freq = new Map(); for (const x of nums) freq.set(x, (freq.get(x) || 0) + 1); const buckets = Array.from({ length: nums.length + 1 }, () => []); for (const [v, f] of freq) buckets[f].push(v); const ans = []; for (let f = buckets.length - 1; f >= 0 && ans.length < k; f--) { for (const v of buckets[f]) { ans.push(v); if (ans.length === k) break; } } return ans;};class Solution { public int[] topKFrequent(int[] nums, int k) { Map<Integer, Integer> freq = new HashMap<>(); for (int x : nums) freq.merge(x, 1, Integer::sum); List<List<Integer>> buckets = new ArrayList<>(); for (int i = 0; i <= nums.length; i++) buckets.add(new ArrayList<>()); for (Map.Entry<Integer, Integer> e : freq.entrySet()) { buckets.get(e.getValue()).add(e.getKey()); } int[] ans = new int[k]; int idx = 0; for (int f = buckets.size() - 1; f >= 0 && idx < k; f--) { for (int v : buckets.get(f)) { ans[idx++] = v; if (idx == k) break; } } return ans; }}function topKFrequent(nums: number[], k: number): number[] { const freq: Map<number, number> = new Map(); for (const x of nums) freq.set(x, (freq.get(x) || 0) + 1); const buckets: number[][] = Array.from({ length: nums.length + 1 }, () => []); for (const [v, f] of freq) buckets[f].push(v); const ans: number[] = []; for (let f = buckets.length - 1; f >= 0 && ans.length < k; f--) { for (const v of buckets[f]) { ans.push(v); if (ans.length === k) break; } } return ans;}Editorial#
The key observation: frequency ∈ [1, n], so bucketing by frequency gives O(n) total. Walk buckets right-to-left until we’ve collected k items. Beats the heap approach by a log k factor.
Complexity#
- Time: O(n).
- Space: O(n).
Concept revision#
Related#