Top K Frequent Elements Using Heap in DSA C++ - Time & Space Complexity
We want to understand how the time needed grows when finding the top K frequent elements using a heap.
How does the number of steps change as the input list gets bigger?
Analyze the time complexity of the following code snippet.
#include <vector>
#include <unordered_map>
#include <queue>
#include <utility>
std::vector<int> topKFrequent(std::vector<int>& nums, int k) {
std::unordered_map<int, int> freq;
for (int num : nums) freq[num]++;
auto cmp = [](std::pair<int,int> a, std::pair<int,int> b) { return a.second > b.second; };
std::priority_queue<std::pair<int,int>, std::vector<std::pair<int,int>>, decltype(cmp)> minHeap(cmp);
for (auto& p : freq) {
minHeap.push(p);
if (minHeap.size() > k) minHeap.pop();
}
std::vector<int> result;
while (!minHeap.empty()) {
result.push_back(minHeap.top().first);
minHeap.pop();
}
return result;
}
This code counts frequencies, then uses a min-heap to keep track of the top K frequent elements.
Identify the loops, recursion, array traversals that repeat.
- Primary operation: Loop over all elements to count frequency, then loop over unique elements to push/pop in heap.
- How many times: Counting runs once over all n elements; heap operations run once per unique element (m times).
As input size n grows, counting frequencies grows linearly. Heap operations depend on unique elements m and k.
| Input Size (n) | Approx. Operations |
|---|---|
| 10 (m ≤ 10) | ~10 (count) + 10 * log k (heap) |
| 100 (m ≤ 100) | ~100 + 100 * log k |
| 1000 (m ≤ 1000) | ~1000 + 1000 * log k |
Counting grows directly with n; heap operations grow with number of unique elements times log k, which is usually smaller than n.
Time Complexity: O(n + m log k)
This means the time grows linearly with input size plus the cost of managing a heap of size k for unique elements.
[X] Wrong: "The heap operations take O(n log n) because we push all elements into the heap."
[OK] Correct: We only push unique elements (m), not all n elements, and the heap size is limited to k, so operations are O(m log k), not O(n log n).
Understanding this complexity helps you explain efficient ways to find top frequent items, a common real-world problem.
"What if we used a max-heap of size m instead of a min-heap of size k? How would the time complexity change?"
Practice
Top K Frequent Elements problem?Solution
Step 1: Understand the problem goal
The problem requires finding the k elements that appear most frequently.Step 2: Role of heap in this context
A heap helps efficiently keep track of the top k frequent elements by maintaining a structure where the smallest frequency is easily accessible and can be replaced if a more frequent element is found.Final Answer:
To efficiently keep track of the top k elements by frequency -> Option AQuick Check:
Heap = Efficient top k tracking [OK]
- Thinking heap sorts all elements fully
- Confusing heap with simple array storage
- Assuming heap removes duplicates
Top K Frequent Elements problem?Solution
Step 1: Identify heap implementation in C++ STL
C++ STL uses a vector with heap algorithms like std::make_heap, std::pop_heap, and std::push_heap to implement heaps.Step 2: Check other container suitability
std::list and std::queue do not support heap operations directly. std::map is a balanced tree, not a heap.Final Answer:
std::vector with std::make_heap and std::pop_heap -> Option AQuick Check:
Vector + heap algorithms = min-heap [OK]
- Choosing std::map thinking it is a heap
- Using std::queue which is FIFO, not heap
- Using std::list which lacks random access
nums = {1,1,1,2,2,3} and k = 2?
std::vector<int> nums = {1,1,1,2,2,3};
int k = 2;
// Frequency map
std::unordered_map freq;
for (int n : nums) freq[n]++;
// Min heap to store pairs (frequency, element)
std::priority_queue, std::vector>, std::greater<>> minHeap;
for (auto& p : freq) {
minHeap.push({p.second, p.first});
if (minHeap.size() > k) minHeap.pop();
}
std::vector<int> result;
while (!minHeap.empty()) {
result.push_back(minHeap.top().second);
minHeap.pop();
}
std::reverse(result.begin(), result.end());
for (int x : result) std::cout << x << " ";Solution
Step 1: Calculate frequencies
Frequencies: 1 -> 3 times, 2 -> 2 times, 3 -> 1 time.Step 2: Use min heap to keep top 2 frequent elements
Insert (3,1), (2,2), (1,3). When size exceeds 2, pop smallest frequency. After all insertions, heap contains (2,2) and (3,1).Step 3: Extract and reverse result
Extracting gives elements 2 and 1, reversed to 1 2.Final Answer:
1 2 -> Option DQuick Check:
Top 2 frequent = 1 and 2 [OK]
- Not reversing the result vector
- Confusing element and frequency in pairs
- Popping wrong element from heap
std::unordered_map freq;
for (int n : nums) freq[n]++;
std::priority_queue> maxHeap;
for (auto& p : freq) {
maxHeap.push({p.second, p.first});
if (maxHeap.size() > k) maxHeap.pop();
}
std::vector<int> result;
while (!maxHeap.empty()) {
result.push_back(maxHeap.top().second);
maxHeap.pop();
}Solution
Step 1: Understand heap type and popping logic
maxHeap stores pairs with highest frequency on top. Popping when size > k removes the top (most frequent) element, which is wrong.Step 2: Correct approach
Use a minHeap to keep smallest frequency on top and pop it when size > k, so top k frequent remain.Final Answer:
Using maxHeap and popping when size > k removes most frequent elements -> Option BQuick Check:
MaxHeap pop removes top freq, wrong for top k [OK]
- Using maxHeap and popping top element
- Not reversing result vector if needed
- Ignoring frequency-element pair structure
Top K Frequent Elements always returns the smallest elements when frequencies tie? Choose the best approach.Solution
Step 1: Understand tie-breaking requirement
When frequencies tie, we want the smaller element to come first in the top k.Step 2: Modify heap ordering
Use a min-heap that orders pairs by frequency first, then by element value ascending to break ties correctly.Step 3: Why other options fail
Max-heap ignores tie-break order, sorting entire array is inefficient, and hash map alone cannot find top k efficiently.Final Answer:
Use a min-heap ordered by frequency, then by element value ascending -> Option CQuick Check:
Min-heap with tie-break by element value [OK]
- Ignoring tie-break condition in heap
- Sorting entire array wastes time
- Using hash map alone without heap
Ready to prove it?
Pass the test and this pattern moves unknown
