21. Top K Frequent Elements in Array
β GFG solution to the Top K Frequent Elements problem: find k most frequent elements using QuickSelect partitioning with optimal time complexity. π
The problem can be found at the following link: π Question Link
π§© Problem Description
You are given a non-empty integer array arr[]
. Your task is to find and return the top k elements which have the highest frequency in the array.
Note: If two numbers have the same frequency, the larger number should be given the higher priority.
π Examples
Example 1
Input: arr[] = [3, 1, 4, 4, 5, 2, 6, 1], k = 2
Output: [4, 1]
Explanation: Frequency of 4 is 2 and frequency of 1 is 2, these two have the maximum
frequency and 4 is larger than 1.
Example 2
Input: arr[] = [7, 10, 11, 5, 2, 5, 5, 7, 11, 8, 9], k = 4
Output: [5, 11, 7, 10]
Explanation: Frequency of 5 is 3, frequency of 11 is 2, frequency of 7 is 2,
frequency of 10 is 1.
π Constraints
$1 \le \text{arr.size()} \le 10^5$
$1 \le \text{arr}[i] \le 10^5$
$1 \le k \le \text{no. of distinct elements}$
β
My Approach
The optimal approach uses QuickSelect Partitioning combined with Hash Map frequency counting to efficiently find the top k frequent elements:
QuickSelect + Partial Sort Algorithm
Build Frequency Map:
Traverse the entire array once to count the frequency of each element.
Store frequencies in an unordered hash map for O(1) average lookup time.
Time: O(n) where n is the array size.
Create Frequency-Element Pairs:
Convert the frequency map into a vector of pairs.
Each pair contains (frequency, element) for easy sorting and comparison.
This transformation takes O(m) time where m is the number of unique elements.
Partition Using nth_element:
Apply C++ STL's
nth_element
function with greater comparator.This uses the QuickSelect algorithm to partition the vector.
After partitioning, the top k frequent elements are guaranteed to be in the first k positions.
Average time complexity: O(m) for partitioning.
Sort Top K Elements:
Sort only the first k elements in descending order by frequency.
When frequencies are equal, the larger element value gets priority.
This ensures the correct order as per problem requirements.
Time: O(k log k) which is typically much smaller than O(m log m).
Extract Result:
Iterate through the first k sorted pairs.
Extract only the element values (second component of pair) into result vector.
Return the final result containing top k frequent elements.
Why This Approach?
Optimal Time Complexity: Combines the efficiency of QuickSelect (O(m) average) with minimal sorting overhead (O(k log k)).
Space Efficient: Uses only O(m) extra space for frequency map and pairs vector.
Handles Tie-Breaking: Automatically prioritizes larger values when frequencies are equal through pair comparison.
Scalable: Performs exceptionally well when k << m (k is much smaller than unique elements count).
π Time and Auxiliary Space Complexity
Expected Time Complexity: O(n + m + k log k), where n is the size of the input array, m is the number of distinct elements, and k is the number of top elements to find. Breaking down: O(n) for frequency counting, O(m) average for nth_element partitioning, and O(k log k) for sorting the top k elements. Since typically k << m << n, the overall complexity is highly efficient.
Expected Auxiliary Space Complexity: O(m), where m is the number of distinct elements in the array. We use a hash map to store element frequencies (O(m)) and a vector of pairs to store frequency-element combinations (O(m)). The result vector uses O(k) space which is included in O(m) since k β€ m.
π§βπ» Code (C++)
class Solution {
public:
vector<int> topKFreq(vector<int>& arr, int k) {
unordered_map<int, int> freq;
for (int x : arr) freq[x]++;
vector<pair<int, int>> v;
for (auto& p : freq) v.push_back({p.second, p.first});
nth_element(v.begin(), v.begin() + k - 1, v.end(), greater<pair<int, int>>());
sort(v.begin(), v.begin() + k, greater<pair<int, int>>());
vector<int> res;
for (int i = 0; i < k; i++) {
res.push_back(v[i].second);
}
return res;
}
};
β Code (Java)
class Solution {
public ArrayList<Integer> topKFreq(int[] a, int k) {
HashMap<Integer, Integer> f = new HashMap<>();
for (int x : a) f.put(x, f.getOrDefault(x, 0) + 1);
List<int[]> v = new ArrayList<>();
for (var e : f.entrySet()) v.add(new int[]{e.getValue(), e.getKey()});
v.sort((x, y) -> y[0] == x[0] ? y[1] - x[1] : y[0] - x[0]);
ArrayList<Integer> r = new ArrayList<>();
for (int i = 0; i < k; i++) r.add(v.get(i)[1]);
return r;
}
}
π Code (Python)
from collections import Counter
class Solution:
def topKFreq(self, a, k):
f = Counter(a)
return [x for x, _ in sorted(f.items(), key=lambda x: (-x[1], -x[0]))[:k]]
π§ Contribution and Support
For discussions, questions, or doubts related to this solution, feel free to connect on LinkedIn: π¬ Any Questions?. Let's make this learning journey more collaborative!
β If you find this helpful, please give this repository a star! β
πVisitor Count
Last updated