Median of Data Stream Using Two Heaps in DSA Go - 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 numbers and get the middle change as more numbers arrive?
Analyze the time complexity of the following code snippet.
// Add a number to the data stream
func addNum(num int) {
if maxHeap.Len() == 0 || num <= maxHeap.Peek() {
maxHeap.Push(num)
} else {
minHeap.Push(num)
}
// Balance the heaps
if maxHeap.Len() > minHeap.Len()+1 {
minHeap.Push(maxHeap.Pop())
} else if minHeap.Len() > maxHeap.Len() {
maxHeap.Push(minHeap.Pop())
}
}
// Find the median
func findMedian() float64 {
if maxHeap.Len() > minHeap.Len() {
return float64(maxHeap.Peek())
}
return float64(maxHeap.Peek()+minHeap.Peek()) / 2.0
}
This code adds numbers to two heaps and keeps them balanced to quickly find the median.
Identify the loops, recursion, array traversals that repeat.
- Primary operation: Inserting into heaps and balancing them.
- How many times: Each new number causes one insert and possibly one move between heaps.
Each new number requires a few steps that take about the same time regardless of total numbers.
| Input Size (n) | Approx. Operations |
|---|---|
| 10 | About 10 insertions and balancing steps |
| 100 | About 100 insertions and balancing steps |
| 1000 | About 1000 insertions and balancing steps |
Pattern observation: The work grows steadily with the number of inputs, but each step stays efficient.
Time Complexity: O(log n)
This means adding each number takes a little more time as the list grows, but still stays fast.
[X] Wrong: "Adding a number is O(1) because it's just one step."
[OK] Correct: Because heaps need to keep order, adding a number requires rearranging which takes time growing with the heap size.
Knowing how to keep track of the middle value quickly is a useful skill for many real-time data problems.
"What if we used a sorted list 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 CQuick Check:
Two heaps split data halves = A [OK]
- Thinking heaps sort all numbers fully
- Using one heap only
- Confusing max-heap and min-heap roles
Solution
Step 1: Recognize heap initialization in Go
Heaps are initialized as pointers to custom heap types that implement heap.Interface.Step 2: Identify correct syntax
Using &IntMaxHeap{} and &IntMinHeap{} creates pointers to heap structs ready for heap operations.Final Answer:
maxHeap := &IntMaxHeap{}; minHeap := &IntMinHeap{} -> Option AQuick Check:
Pointer to heap struct = A [OK]
- Using heap.New which does not exist
- Swapping max-heap and min-heap types
- Initializing as slices without heap.Interface
5, 15, 1, 3, what is the median after all insertions using two heaps?Solution
Step 1: Insert numbers and balance heaps
Insert 5 (maxHeap top=5); insert 15 (minHeap top=15); insert 1 ≤5 (maxHeap top=5, contains 5,1); insert 3 ≤5 (maxHeap top=5, contains 5,1,3), balance: len=3 >1+1, pop 5 -> push minHeap top=5 (15,5), maxHeap top=3 (1,3).Step 2: Calculate median from heaps
Heaps sizes equal (2 each). Median = average of maxHeap top (3) and minHeap top (5) = (3+5)/2 = 4.Final Answer:
4 -> Option DQuick Check:
Median = (3+5)/2 = 4 [OK]
- Taking median as maxHeap top only
- Not balancing heaps after insertion
- Calculating median as sum instead of average
func addNum(num int) {
if maxHeap.Len() == 0 || num <= (*maxHeap)[0] {
heap.Push(maxHeap, num)
} else {
heap.Push(minHeap, num)
}
if maxHeap.Len() > minHeap.Len()+1 {
heap.Push(minHeap, heap.Pop(maxHeap))
} else if minHeap.Len() > maxHeap.Len() {
heap.Push(maxHeap, heap.Pop(minHeap))
}
}Solution
Step 1: Analyze balancing logic
The first condition correctly balances when maxHeap is too large (> minHeap.Len()+1). But the second condition checks minHeap.Len() > maxHeap.Len(), which triggers rebalance correctly when minHeap is larger.Step 2: Why heaps are not balanced
Original code used > maxHeap.Len() + 1 which fails to rebalance when minHeap is exactly one larger. Changing to > maxHeap.Len() fixes this.Final Answer:
Heaps are not balanced after insertion -> Option AQuick Check:
minHeap balance: > max.Len() [OK]
- Using +1 symmetrically for both balance conditions
- Wrong size difference in minHeap check
- Not balancing after every insertion
Solution
Step 1: Choose correct heap types for halves
Max-heap stores lower half (including negatives), min-heap stores upper half, handling all values correctly.Step 2: Balance heaps and compute median
Keep heaps balanced in size; median is top of max-heap if odd count, or average of tops if even count.Final Answer:
Use a max-heap for lower half and min-heap for upper half, balance sizes, and calculate median from tops -> Option BQuick Check:
Two heaps balanced handle negatives and duplicates [OK]
- Using two min-heaps which can't split halves properly
- Sorting entire stream each time (inefficient)
- Using single heap only
Ready to prove it?
Pass the test and this pattern moves unknown
