Median of Data Stream Using Two Heaps in DSA Java - 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.
import java.util.PriorityQueue;
class MedianFinder {
PriorityQueue<Integer> lowers = new PriorityQueue<>((a, b) -> b - a); // max heap
PriorityQueue<Integer> highers = new PriorityQueue<>(); // min heap
public void addNum(int num) {
if (lowers.isEmpty() || num <= lowers.peek()) lowers.add(num);
else highers.add(num);
if (lowers.size() - highers.size() > 1) highers.add(lowers.poll());
else if (highers.size() - lowers.size() > 1) lowers.add(highers.poll());
}
public double findMedian() {
if (lowers.size() > highers.size()) return lowers.peek();
else if (highers.size() > lowers.size()) return highers.peek();
else return (lowers.peek() + highers.peek()) / 2.0;
}
}
This code keeps two heaps to quickly find the median as numbers are added.
Identify the loops, recursion, array traversals that repeat.
- Primary operation: Adding a number to one of the two heaps and balancing them.
- How many times: Each addNum call does a few heap operations, each taking time related to heap size.
Each new number is added to a heap and sometimes causes a rebalance between heaps.
| Input Size (n) | Approx. Operations |
|---|---|
| 10 | About 10 insertions and possible rebalances, each small and fast. |
| 100 | About 100 insertions, each heap operation slightly slower but still quick. |
| 1000 | About 1000 insertions, heap operations take a bit longer but grow slowly. |
Pattern observation: The time per insertion grows slowly as the heaps get bigger, not linearly.
Time Complexity: O(log n)
This means adding each number and updating the median takes time that grows slowly with the total numbers seen.
[X] Wrong: "Adding a number is constant time because it's just one insert."
[OK] Correct: Inserting into a heap requires rearranging elements to keep order, 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 median calculation
By balancing these heaps, the median can be found quickly from the top elements of the heaps.Final Answer:
To efficiently keep track of the lower half and upper half of the numbers for median calculation -> Option CQuick Check:
Two heaps split data for median [OK]
- Thinking heaps sort all numbers completely
- Using only one heap for median
- Confusing max heap and min heap roles
Solution
Step 1: Identify max heap initialization
Java's PriorityQueue is a min heap by default; to make max heap, use Collections.reverseOrder().Step 2: Check options
PriorityQueue<Integer> maxHeap = new PriorityQueue<>(Collections.reverseOrder()); uses Collections.reverseOrder(), which correctly creates a max heap.Final Answer:
PriorityQueue<Integer> maxHeap = new PriorityQueue<>(Collections.reverseOrder()); -> Option AQuick Check:
Max heap needs reverse order comparator [OK]
- Using default PriorityQueue without comparator
- Using naturalOrder() which is min heap
- Using Collections.naturalOrder() which doesn't exist
MedianFinder mf = new MedianFinder(); mf.addNum(1); mf.addNum(5); mf.addNum(2); System.out.println(mf.findMedian());
Assuming the MedianFinder class uses two heaps correctly, what will be the printed output?
Solution
Step 1: Add numbers and balance heaps
Numbers added: 1, 5, 2. Sorted order: 1, 2, 5. Median is middle number 2.Step 2: Calculate median
Since odd count, median is the root of the heap with one extra element, which is 2.Final Answer:
2 -> Option BQuick Check:
Median of [1,2,5] is 2 [OK]
- Calculating average instead of middle for odd count
- Ignoring heap balancing
- Confusing median with mean
public void addNum(int num) {
if (maxHeap.isEmpty() || num <= maxHeap.peek()) {
maxHeap.add(num);
} else {
minHeap.add(num);
}
if (maxHeap.size() > minHeap.size() + 1) {
minHeap.add(maxHeap.poll());
} else if (minHeap.size() > maxHeap.size()) {
maxHeap.add(minHeap.poll());
}
}Solution
Step 1: Analyze addNum logic
It adds to maxHeap if empty or num <= maxHeap.peek(), else to minHeap.Step 2: Check rebalance conditions
Rebalances if maxHeap is bigger by more than 1 or minHeap bigger than maxHeap.Final Answer:
No bug, method works correctly -> Option DQuick Check:
Correct heap balancing logic [OK]
- Forgetting to check maxHeap empty before peek
- Incorrect rebalance size conditions
- Misplacing elements between heaps
Solution
Step 1: Evaluate approaches for median calculation
Sorting after each insertion (Store all numbers in a list and sort it after each insertion) is costly (O(n log n)). Single heap or hash map do not support quick median.Step 2: Explain two heaps efficiency
Two heaps keep halves balanced, allowing O(log n) insertion and O(1) median retrieval.Final Answer:
Use two balanced heaps to keep halves and rebalance after each insertion -> Option AQuick Check:
Two heaps optimize median in data stream [OK]
- Sorting entire list each time
- Using single heap only
- Ignoring insertion cost
Ready to prove it?
Pass the test and this pattern moves unknown
