Median of Data Stream Using Two Heaps in DSA C - Time & Space Complexity
We want to understand how fast we can find the middle value when numbers keep coming in.
How does the time to add a number and get the median grow as more numbers arrive?
Analyze the time complexity of the following code snippet.
// Insert number into heaps and rebalance
void addNum(int num) {
if (maxHeap.size() == 0 || num <= maxHeap.top()) {
maxHeap.push(num);
} else {
minHeap.push(num);
}
// Rebalance heaps
if (maxHeap.size() > minHeap.size() + 1) {
minHeap.push(maxHeap.top());
maxHeap.pop();
} else if (minHeap.size() > maxHeap.size()) {
maxHeap.push(minHeap.top());
minHeap.pop();
}
}
// Get median
double findMedian() {
if (maxHeap.size() > minHeap.size())
return maxHeap.top();
else
return (maxHeap.top() + minHeap.top()) / 2.0;
}
This code adds numbers to two heaps to keep track of the median efficiently.
Identify the loops, recursion, array traversals that repeat.
- Primary operation: Inserting into heaps and rebalancing them.
- How many times: Each new number causes one insertion and possibly one rebalance.
Each new number is added with a small fixed cost that grows slowly as the heaps get bigger.
| Input Size (n) | Approx. Operations |
|---|---|
| 10 | About 10 insertions, each taking a few steps. |
| 100 | About 100 insertions, each a bit longer but still quick. |
| 1000 | About 1000 insertions, each taking more steps but still efficient. |
Pattern observation: The time per insertion grows slowly, roughly with the height of the heaps, which grows as the logarithm of the number of elements.
Time Complexity: O(log n)
This means adding each number and keeping track of the median takes time that grows slowly as the list gets longer.
[X] Wrong: "Adding a number takes constant time because it's just one push."
[OK] Correct: Heaps need to keep order, so pushing a number may move it up the heap, which takes time proportional to the heap's height, growing with the number of elements.
Understanding how to keep track of medians quickly is a useful skill that shows you can manage data efficiently as it changes.
"What if we used a balanced binary search tree instead of two heaps? How would the time complexity change?"
Practice
Solution
Step 1: Understand the role of two heaps
The max-heap stores the smaller half of the numbers, and the min-heap stores the larger half.Step 2: Explain how this helps find the median
By balancing these heaps, the median is either the top of one heap or the average of the tops of both heaps.Final Answer:
To efficiently keep track of the lower half and upper half of the numbers separately -> Option DQuick Check:
Two heaps split data halves = To efficiently keep track of the lower half and upper half of the numbers separately [OK]
- Thinking heaps sort all data fully
- Using one heap only
- Confusing heaps with arrays
Solution
Step 1: Decide where to insert the new number
If the new number is less than or equal to the max-heap top, insert into max-heap; otherwise, insert into min-heap.Step 2: Balance the heaps if needed
After insertion, balance the heaps so their sizes differ by at most one.Final Answer:
Insert into the heap based on comparison with max-heap top -> Option AQuick Check:
Compare with max-heap top to choose heap [OK]
- Always inserting into one heap without condition
- Not balancing heaps after insertion
- Inserting into wrong heap based on min-heap top
Insert 5 Insert 15 Insert 1 Insert 3 Find median
Solution
Step 1: Insert numbers and balance heaps
Insert 5 into max-heap ([5]); insert 15 into min-heap ([15]); insert 1 (<=5) into max-heap ([5,1] top=5); insert 3 (<=5) into max-heap ([5,3,1] top=5), sizes 3 vs 1, balance: min.push(5) ([15,5] top=5), max.pop() ([3,1] top=3).Step 2: Calculate median from heaps
Heaps sizes equal (2 and 2), median = average of max-heap top (3) and min-heap top (5) = (3+5)/2 = 4.Final Answer:
4 -> Option CQuick Check:
Median of [1,3,5,15] = 4 [OK]
- Taking median as max-heap top only
- Not balancing heaps after insertions
- Calculating median incorrectly as 5 or 3
void addNumber(int num, priority_queue<int>& maxHeap, priority_queue& minHeap) {
if (maxHeap.empty() || num <= maxHeap.top()) {
maxHeap.push(num);
} else {
minHeap.push(num);
}
if (maxHeap.size() > minHeap.size() + 1) {
minHeap.push(maxHeap.top());
maxHeap.pop();
} else if (minHeap.size() > maxHeap.size() + 1) {
maxHeap.push(minHeap.top());
minHeap.pop();
}
}Solution
Step 1: Analyze balancing logic
When balancing, the code pops the top element of one heap before pushing the new top to the other, which moves the wrong element.Step 2: Explain correct balancing
Correct approach is to push heap.top() first, then pop() to transfer the correct top element.Final Answer:
The balancing pops top element before pushing, causing wrong order -> Option AQuick Check:
Push top before pop to balance heaps correctly [OK]
- Popping before pushing the correct top
- Ignoring heap size difference conditions
- Not handling empty heaps properly
Solution
Step 1: Understand heap size difference
When total elements are odd, one heap will have one more element than the other.Step 2: Determine median from heaps
The median is the top element of the heap that has the extra element, because it holds the middle value.Final Answer:
The median is the top element of the heap with one extra element -> Option BQuick Check:
Odd count median = top of larger heap [OK]
- Averaging tops when count is odd
- Assuming max-heap always has extra element
- Ignoring heap size difference
Ready to prove it?
Pass the test and this pattern moves unknown
