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 middle change as more numbers arrive?
Analyze the time complexity of the following code snippet.
public class MedianFinder {
private readonly SortedSet lowers = new(); // max heap behavior
private readonly SortedSet highers = new(); // min heap behavior
public void AddNum(int num) {
if (lowers.Count == 0 || num <= lowers.Max) lowers.Add(num);
else highers.Add(num);
BalanceHeaps();
}
private void BalanceHeaps() {
if (lowers.Count > highers.Count + 1) {
highers.Add(lowers.Max);
lowers.Remove(lowers.Max);
} else if (highers.Count > lowers.Count) {
lowers.Add(highers.Min);
highers.Remove(highers.Min);
}
}
public double FindMedian() {
if (lowers.Count == highers.Count) return (lowers.Max + highers.Min) / 2.0;
return lowers.Max;
}
}
This code keeps two groups of numbers to quickly find the middle value as new numbers come in.
Identify the loops, recursion, array traversals that repeat.
- Primary operation: Adding a number to one of the two heaps (balanced sets) and balancing them.
- How many times: Each new number causes one add and possibly one remove operation on a heap.
Each new number is added and balanced with a few steps that depend on the size of the heaps.
| 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 roughly in a straight line with the number of inputs.
Time Complexity: O(n log n)
This means adding n numbers takes time that grows a bit faster than n, because each add involves a quick sorting step.
[X] Wrong: "Adding a number is always O(1) because it's just putting it somewhere."
[OK] Correct: Actually, adding to a heap or balanced set takes time related to the size of the data because it keeps things sorted.
Understanding this helps you show how to handle data that changes over time efficiently, a useful skill in many real problems.
"What if we used a simple list and sorted it every time we add a number? 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: How this helps find the median
By balancing these heaps, the median is either the top of one heap or the average of both tops.Final Answer:
To efficiently find the median by balancing smaller and larger halves of the data -> Option DQuick Check:
Two heaps balance halves = median found efficiently [OK]
- Thinking heaps sort all data fully
- Using only one heap for median
- Confusing median with mean
Solution
Step 1: Understand PriorityQueue default behavior
By default, PriorityQueue in C# is a min-heap based on priority values.Step 2: Creating a max-heap requires reversing comparison
Using Comparer<int>.Create((a, b) => b - a) reverses order to make a max-heap.Final Answer:
var maxHeap = new PriorityQueue<int, int>(Comparer<int>.Create((a, b) => b - a)); -> Option BQuick Check:
Reverse comparer creates max-heap [OK]
- Using default comparer creates min-heap, not max-heap
- Confusing type parameters in PriorityQueue
- Not providing comparer for max-heap
Solution
Step 1: Insert numbers and balance heaps
Insert 5 -> maxHeap=[5]; 15 -> minHeap=[15]; 1 ≤ max.top(5) -> maxHeap=[5,1] top=5; 3 ≤5 -> maxHeap=[5,1,3] top=5 (size 3 vs 1). Balance: move max.top=5 to minHeap -> maxHeap=[3,1] top=3, minHeap=[15,5] top=5.Step 2: Calculate median from heaps
maxHeap top = 3, minHeap top = 5. Median = (3 + 5) / 2 = 4.Final Answer:
4 -> Option AQuick Check:
Median of [1,3,5,15] = 4 [OK]
- Not balancing heaps after each insertion
- Taking median as middle element without averaging
- Mixing up maxHeap and minHeap tops
if (maxHeap.Count == 0 || num <= maxHeap.Peek())
maxHeap.Enqueue(num, num);
else
minHeap.Enqueue(num, num);
// Balance heaps
if (maxHeap.Count > minHeap.Count + 1)
minHeap.Enqueue(maxHeap.Dequeue(), maxHeap.Dequeue());
else if (minHeap.Count > maxHeap.Count)
maxHeap.Enqueue(minHeap.Dequeue(), minHeap.Dequeue());Solution
Step 1: Analyze balancing code
When balancing, code calls Dequeue() twice to move one element, which removes two elements incorrectly.Step 2: Correct balancing approach
Only one Dequeue() should be called to move one element from one heap to the other.Final Answer:
Calling Dequeue() twice when balancing heaps causes errors -> Option CQuick Check:
Double Dequeue removes two elements instead of one [OK]
- Calling Dequeue twice instead of once
- Ignoring heap size difference rules
- Incorrect comparison operators for insertion
Solution
Step 1: Understand handling of duplicates in heaps
Duplicates are stored like any other number; heaps do not remove duplicates automatically.Step 2: Insert numbers and find median
Numbers: [2, 2, 3, 4, 4, 4]. Sorted: [2, 2, 3, 4, 4, 4]. Median is average of 3rd and 4th elements: (3 + 4) / 2 = 3.5.Final Answer:
Duplicates are stored normally; median is 3.5 -> Option AQuick Check:
Duplicates stored; median of [2,2,3,4,4,4] = 3.5 [OK]
- Assuming duplicates are removed automatically
- Calculating median incorrectly with duplicates
- Confusing median with mode
Ready to prove it?
Pass the test and this pattern moves unknown
