Find Median from Data Stream hard
Problem Statement
Design a data structure that supports the following two operations:
void addNum(int num)
: Add a number num to the data structure.double findMedian()
: Return the median of all elements so far in the data structure.
Example 1:
MedianFinder medianFinder = new MedianFinder();
medianFinder.addNum(1); // arr = [1]
medianFinder.addNum(2); // arr = [1, 2]
medianFinder.findMedian(); // return 1.5 (because median is (1 + 2) / 2 = 1.5)
medianFinder.addNum(3); // arr = [1, 2, 3]
medianFinder.findMedian(); // return 2.0
Example 2:
MedianFinder medianFinder = new MedianFinder();
medianFinder.addNum(1); // arr = [1]
medianFinder.addNum(2); // arr = [1,2]
medianFinder.addNum(3); // arr = [1,2,3]
medianFinder.addNum(4); // arr = [1,2,3,4]
medianFinder.findMedian(); // return 2.5
Steps
To efficiently find the median, we'll use two heaps: a max-heap (minHeap
) to store the smaller half of the numbers, and a min-heap (maxHeap
) to store the larger half. We maintain the invariant that minHeap.length <= maxHeap.length <= minHeap.length + 1
. This ensures that the median is either the top of the maxHeap
or the average of the tops of both heaps.
Explanation
-
addNum(num)
:- If
maxHeap
is empty ornum
is less than or equal to the top ofmaxHeap
, addnum
tomaxHeap
. Otherwise, addnum
tominHeap
. - After adding, balance the heaps: if
maxHeap.length > minHeap.length + 1
, move the largest element frommaxHeap
tominHeap
. IfminHeap.length > maxHeap.length
, move the smallest element fromminHeap
tomaxHeap
.
- If
-
findMedian()
:- If the total number of elements is odd, the median is the top of
maxHeap
. - If the total number of elements is even, the median is the average of the tops of
maxHeap
andminHeap
.
- If the total number of elements is odd, the median is the top of
Code
class MedianFinder {
minHeap: number[] = [];
maxHeap: number[] = [];
addNum(num: number): void {
if (this.maxHeap.length === 0 || num <= this.maxHeap[0]) {
this.maxHeap.push(num);
this.maxHeap.sort((a, b) => b - a); // Keep maxHeap sorted
} else {
this.minHeap.push(num);
this.minHeap.sort((a, b) => a - b); //Keep minHeap sorted
}
//Balance heaps
if (this.maxHeap.length > this.minHeap.length + 1) {
this.minHeap.push(this.maxHeap.shift()!);
this.minHeap.sort((a, b) => a - b);
} else if (this.minHeap.length > this.maxHeap.length) {
this.maxHeap.push(this.minHeap.shift()!);
this.maxHeap.sort((a, b) => b - a);
}
}
findMedian(): number {
if (this.maxHeap.length === this.minHeap.length) {
return (this.maxHeap[0] + this.minHeap[0]) / 2;
} else {
return this.maxHeap[0];
}
}
}
/**
* Your MedianFinder object will be instantiated and called as such:
* var obj = new MedianFinder()
* obj.addNum(num)
* var param_2 = obj.findMedian()
*/
Complexity
-
Time Complexity:
addNum(num)
: O(log n) due to heap operations (insertion and balancing).findMedian()
: O(1) - accessing the top of the heap is constant time.
-
Space Complexity: O(n) to store the elements in the heaps. The heaps themselves take linear space in the worst case.