Skip to content
JZLeetCode
Go back

LeetCode 347 Top K Frequent Elements

Table of contents

Open Table of contents

Description

Given an integer array nums and an integer k, return the k most frequent elements. You may return the answer in any order.

Example 1:

Input: nums = [1,1,1,2,2,3], k = 2
Output: [1,2]

Example 2:

Input: nums = [1], k = 1
Output: [1]

Constraints:

1 <= nums.length <= 10^5
-10^4 <= nums[i] <= 10^4
k is in the range [1, the number of unique elements in the array].
It is guaranteed that the answer is unique.

Follow up: Your algorithm's time complexity must be better than O(n log n), where n is the array's size.

Solution 1: Bucket Sort

Idea

Use a frequency count, then distribute elements into buckets where the index represents the frequency. Collect results from the highest-frequency bucket downward until we have k elements.

Input: nums = [1,1,1,2,2,3], k = 2

Step 1 - Count frequencies:
  count = {1:3, 2:2, 3:1}

Step 2 - Create buckets (index = frequency, max index = n = 6):
  buckets[1] = [3]
  buckets[2] = [2]
  buckets[3] = [1]
  buckets[4..6] = []

Step 3 - Collect from highest bucket down:
  freq=6: empty
  freq=5: empty
  freq=4: empty
  freq=3: pick 1 -> result = [1]
  freq=2: pick 2 -> result = [1, 2]  <- have k=2, done!

Output: [1, 2]

Complexity: Time O(n)O(n) — counting is O(n)O(n), distributing to buckets is O(n)O(n), collecting is O(n)O(n). Space O(n)O(n) for the count map and bucket array.

Java

public static int[] topKFrequentBucket(int[] nums, int k) {
    Map<Integer, Integer> count = new HashMap<>();
    for (int num : nums) count.merge(num, 1, Integer::sum); // O(n)
    @SuppressWarnings("unchecked")
    List<Integer>[] buckets = new List[nums.length + 1]; // index = frequency
    for (var entry : count.entrySet()) { // O(n) distribute
        int freq = entry.getValue();
        if (buckets[freq] == null) buckets[freq] = new ArrayList<>();
        buckets[freq].add(entry.getKey());
    }
    int[] res = new int[k];
    int idx = 0;
    for (int freq = nums.length; freq > 0 && idx < k; freq--) { // O(n) collect
        if (buckets[freq] != null) {
            for (int num : buckets[freq]) {
                res[idx++] = num;
                if (idx == k) return res;
            }
        }
    }
    return res;
}

Python

class Solution:
    def topKFrequent(self, nums: list[int], k: int) -> list[int]:
        count = Counter(nums)
        n = len(nums)
        buckets: list[list[int]] = [[] for _ in range(n + 1)]  # O(n) space
        for num, freq in count.items():  # O(n) distribute to buckets
            buckets[freq].append(num)
        res = []
        for freq in range(n, 0, -1):  # O(n) collect from highest freq
            for num in buckets[freq]:
                res.append(num)
                if len(res) == k:
                    return res
        return res

C++

vector<int> topKFrequent(vector<int> &nums, int k) {
    unordered_map<int, int> count;
    for (int num: nums) count[num]++; // O(n)
    int n = (int) nums.size();
    vector<vector<int>> buckets(n + 1); // index = frequency
    for (auto &[num, freq]: count) buckets[freq].push_back(num); // O(n) distribute
    vector<int> res;
    for (int freq = n; freq > 0 && (int) res.size() < k; freq--) { // O(n) collect
        for (int num: buckets[freq]) {
            res.push_back(num);
            if ((int) res.size() == k) return res;
        }
    }
    return res;
}

Rust

pub fn top_k_frequent_bucket(nums: Vec<i32>, k: i32) -> Vec<i32> {
    let n = nums.len();
    let mut count: HashMap<i32, usize> = HashMap::new();
    for &num in &nums { // O(n)
        *count.entry(num).or_insert(0) += 1;
    }
    let mut buckets: Vec<Vec<i32>> = vec![vec![]; n + 1]; // index = frequency
    for (&num, &freq) in &count { // O(n) distribute
        buckets[freq].push(num);
    }
    let mut res = Vec::with_capacity(k as usize);
    for freq in (1..=n).rev() { // O(n) collect from highest freq
        for &num in &buckets[freq] {
            res.push(num);
            if res.len() == k as usize {
                return res;
            }
        }
    }
    res
}

Solution 2: Min-Heap of Size K

Idea

Count frequencies, then maintain a min-heap of size k. As we iterate through the frequency map, push each element; when the heap exceeds size k, pop the minimum (least frequent). After processing all elements, the heap contains the k most frequent.

Input: nums = [1,1,1,2,2,3], k = 2

Step 1 - Count: {1:3, 2:2, 3:1}

Step 2 - Process with min-heap (size limit = 2):
  push (freq=3, num=1) -> heap: [(3,1)]         size=1 <= k
  push (freq=2, num=2) -> heap: [(2,2),(3,1)]   size=2 <= k
  push (freq=1, num=3) -> heap: [(1,3),(3,1),(2,2)] size=3 > k
    pop min (1,3)      -> heap: [(2,2),(3,1)]   size=2 = k

Result: [2, 1] (order doesn't matter)

Complexity: Time O(nlogk)O(n \log k) — counting is O(n)O(n), iterating unique elements (at most nn) with heap operations each costing O(logk)O(\log k). Space O(n)O(n) for the count map, O(k)O(k) for the heap.

Java

public static int[] topKFrequentHeap(int[] nums, int k) {
    Map<Integer, Integer> count = new HashMap<>();
    for (int num : nums) count.merge(num, 1, Integer::sum); // O(n)
    PriorityQueue<int[]> pq = new PriorityQueue<>(Comparator.comparingInt(a -> a[1])); // min-heap by freq
    for (var entry : count.entrySet()) { // O(n log k)
        pq.offer(new int[]{entry.getKey(), entry.getValue()});
        if (pq.size() > k) pq.poll(); // evict least frequent
    }
    int[] res = new int[k];
    for (int i = 0; i < k; i++) res[i] = pq.poll()[0];
    return res;
}

Python

class Solution2:
    def topKFrequent(self, nums: list[int], k: int) -> list[int]:
        count = Counter(nums)  # O(n) time and space
        heap: list[tuple[int, int]] = []
        for num, freq in count.items():  # O(n log k): iterate n unique, each heap op log k
            heappush(heap, (freq, num))
            if len(heap) > k:
                heappop(heap)  # evict least frequent
        return [num for _, num in heap]

C++

vector<int> topKFrequent(vector<int> &nums, int k) {
    unordered_map<int, int> count;
    for (int num: nums) count[num]++; // O(n)
    // min-heap by frequency
    priority_queue<pair<int, int>, vector<pair<int, int>>, greater<>> pq;
    for (auto &[num, freq]: count) { // O(n log k)
        pq.emplace(freq, num);
        if ((int) pq.size() > k) pq.pop(); // evict least frequent
    }
    vector<int> res;
    while (!pq.empty()) {
        res.push_back(pq.top().second);
        pq.pop();
    }
    return res;
}

Rust

pub fn top_k_frequent(nums: Vec<i32>, k: i32) -> Vec<i32> {
    let k = k as usize;
    let mut count: HashMap<i32, usize> = HashMap::new();
    for &num in &nums { // O(n)
        *count.entry(num).or_insert(0) += 1;
    }
    let mut heap: BinaryHeap<Reverse<(usize, i32)>> = BinaryHeap::new(); // min-heap by freq
    for (&num, &freq) in &count { // O(n log k)
        heap.push(Reverse((freq, num)));
        if heap.len() > k {
            heap.pop(); // evict least frequent
        }
    }
    heap.into_iter().map(|Reverse((_, num))| num).collect()
}
Share this post on:

Previous Post
LeetCode 230 Kth Smallest Element in a BST
Next Post
System Design - How the TLS Handshake Works