[LeetCode 295] Find Median from Data Stream
Median is the middle value in an ordered integer list. If the size of the list is even, there is no middle value. So the median is the mean of the two middle value.
For example,
[2,3,4]
, the median is 3
[2,3]
, the median is (2 + 3) / 2 = 2.5
Design a data structure that supports the following two operations:
- void addNum(int num) - Add a integer number from the data stream to the data structure.
- double findMedian() - Return the median of all elements so far.
Example:
addNum(1)
addNum(2)
findMedian() -> 1.5
addNum(3)
findMedian() -> 2
Follow up:
- If all integer numbers from the stream are between 0 and 100, how would you optimize it?
- If 99% of all integer numbers from the stream are between 0 and 100, how would you optimize it?
Solution 1. Each time a new number is added, sort the new array and get the median.
Runtime: Sum of O(i * log i) for i: [1, n];
Space: O(n)
BUD optimization:
Bottlenecks: To sort an array arr takes O(arr.length * log arr.length) time.
Unecessary work: Sorted the whole array each time even though we don't need all the
non-median numbers to be at its right order in the array.
Solution 2. Apply quick select algorithm to select the new median each time a new number is added.
Runtime: Sum of O(i) for i: [1, n];
Space: O(n)
We've improved the bottleneck in solution 1 from O(i * log i) to O(i). But this solution is still not
optimal. Can we reduce it even further to log(i)?
Solution 3. Use a min and max Priority Queue to dynamically maintain the current median. Adding new number
to a priority queue takes O(log i) time, i is the current number of elements stored in the priority queue.
Runtime: Sum of O(log i) for i: [1, n], which is approximately O(n * log n)
Space: O(n)
Use the following invariants to maintain both max and min pq.
The max pq has 1 more element than the min pq if we've processed an odd number of elements;
They have the same number of elements if we've processed an even number of elements;
class MedianFinder {
private PriorityQueue<Integer> maxPq;
private PriorityQueue<Integer> minPq;
int size;
/** initialize your data structure here. */
public MedianFinder() {
maxPq = new PriorityQueue<>(Collections.reverseOrder());
minPq = new PriorityQueue<>();
size = 0;
}
public void addNum(int num) {
if(size == 0) {
maxPq.add(num);
}
else {
if(num <= maxPq.peek()) {
maxPq.add(num);
}
else {
minPq.add(num);
}
if(maxPq.size() - minPq.size() > 1) {
minPq.add(maxPq.poll());
}
else if(minPq.size() > maxPq.size()) {
maxPq.add(minPq.poll());
}
}
size++;
}
public double findMedian() {
if(size == 0) {
return 0.0;
}
if(size % 2 != 0) {
return maxPq.peek();
}
double sum = maxPq.peek() + minPq.peek();
return sum / 2.0;
}
}
Related Problems
Median
Median of Two Sorted Arrays