Top K Frequent Elements Using Heap in DSA Typescript - 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 work change as the input list gets bigger?
Analyze the time complexity of the following code snippet.
function topKFrequent(nums: number[], k: number): number[] {
const freqMap = new Map();
for (const num of nums) {
freqMap.set(num, (freqMap.get(num) ?? 0) + 1);
}
const heap = new MinHeap<[number, number]>((a, b) => a[1] - b[1]);
for (const [num, freq] of freqMap) {
heap.push([num, freq]);
if (heap.size() > k) heap.pop();
}
return heap.toArray().map(([num]) => num);
}
This code counts how often each number appears, then uses a small heap to keep only the top K frequent numbers.
- Primary operation: Loop over all numbers to count frequency, then loop over unique numbers to push into the heap.
- How many times: First loop runs n times (n = length of input), second loop runs m times (m = number of unique numbers).
As the input size grows, counting frequencies grows linearly. Adding to the heap grows with unique numbers and heap size k.
| Input Size (n) | Approx. Operations |
|---|---|
| 10 | ~10 + 10 * log k |
| 100 | ~100 + m * log k (m ≤ 100) |
| 1000 | ~1000 + m * log k (m ≤ 1000) |
Pattern observation: Counting grows directly with n, heap operations grow with unique count m but only logarithmically with k.
Time Complexity: O(n + m log k)
This means the time grows mostly with input size n and the number of unique elements m times the small heap operations.
[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), and the heap size is limited to k, so each push/pop is O(log k), not O(log n).
Understanding this complexity helps you explain efficient use of heaps and maps, a common pattern in real problems.
"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 goal is to find the K elements that appear most frequently in a list.Step 2: Role of heap in this context
A heap helps efficiently keep track of the top K elements by frequency without sorting the entire list.Final Answer:
To efficiently keep track of the top K frequent elements by frequency -> Option DQuick Check:
Heap = Efficient top K tracking [OK]
- Thinking heap sorts all elements fully
- Confusing heap with simple array storage
- Assuming heap removes duplicates
Solution
Step 1: Identify correct heap initialization syntax
TypeScript with modern libraries uses MinPriorityQueue with a priority function for min-heap.Step 2: Check each option
const minHeap = new MinPriorityQueue<{freq: number, val: number}>({priority: x => x.freq}); correctly creates a min-heap with priority based on frequency. Others are arrays, maps, or sets which do not provide heap behavior.Final Answer:
const minHeap = new MinPriorityQueue<{freq: number, val: number}>({priority: x => x.freq}); -> Option AQuick Check:
MinPriorityQueue with priority function = correct heap [OK]
- Using Array instead of heap
- Using Map or Set which are not heaps
- Missing priority function in heap initialization
nums = [1,1,1,2,2,3] and k = 2?
function topKFrequent(nums: number[], k: number): number[] {
const freqMap = new Map();
for (const num of nums) {
freqMap.set(num, (freqMap.get(num) || 0) + 1);
}
const minHeap = new MinPriorityQueue<{val: number, freq: number}>({priority: x => x.freq});
for (const [num, freq] of freqMap.entries()) {
minHeap.enqueue({val: num, freq});
if (minHeap.size() > k) {
minHeap.dequeue();
}
}
const result = [];
while (minHeap.size() > 0) {
result.push(minHeap.dequeue().element.val);
}
return result.reverse();
}
console.log(topKFrequent([1,1,1,2,2,3], 2));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 (1,3), (2,2), (3,1). When size > 2, remove smallest freq. So 3 with freq 1 is removed.Step 3: Extract elements from heap and reverse
Heap contains (1,3) and (2,2). Extracted order reversed gives [1, 2].Final Answer:
[1, 2] -> Option BQuick Check:
Top 2 frequent = [1, 2] [OK]
- Not reversing result array
- Removing wrong element from heap
- Confusing frequency counts
function topKFrequent(nums: number[], k: number): number[] {
const freqMap = new Map();
for (const num of nums) {
freqMap.set(num, freqMap.get(num) + 1);
}
const minHeap = new MinPriorityQueue<{val: number, freq: number}>({priority: x => x.freq});
for (const [num, freq] of freqMap.entries()) {
minHeap.enqueue({val: num, freq});
if (minHeap.size() > k) {
minHeap.dequeue();
}
}
const result = [];
while (minHeap.size() > 0) {
result.push(minHeap.dequeue().element.val);
}
return result.reverse();
}Solution
Step 1: Check frequency map update line
freqMap.get(num) returns undefined if num not present, so undefined + 1 causes NaN.Step 2: Correct way to update frequency
Use freqMap.get(num) || 0 to default to 0 if undefined, then add 1.Final Answer:
freqMap.get(num) + 1 causes error when num not in map -> Option CQuick Check:
Undefined + 1 = NaN error [OK]
- Not handling undefined in map get
- Removing wrong element from heap
- Thinking reverse is unnecessary
- Replacing Map with Set incorrectly
nums with many elements having the same frequency, how can you modify the heap approach to ensure the Top K Frequent Elements always returns the smallest elements when frequencies tie? Choose the best approach.Solution
Step 1: Understand tie frequency problem
When multiple elements share frequency, we want consistent ordering by element value.Step 2: Modify heap priority to handle ties
Use a comparator that first compares frequency, then element value to break ties, ensuring smallest elements come first.Step 3: Why other options fail
Sorting entire array is inefficient; max-heap without tie-break can give inconsistent results; ignoring ties causes unpredictable output.Final Answer:
Use a heap with priority comparing frequency first, then element value to break ties -> Option AQuick Check:
Priority with tie-break = correct tie handling [OK]
- Sorting entire array wastes time
- Ignoring tie-break leads to wrong output
- Using max-heap without tie-break causes inconsistency
Ready to prove it?
Pass the test and this pattern moves unknown
