Skip to main content

Find Median from Data Stream

Problem

The median is the middle value in an ordered integer list. If the size of the list is even, there is no middle value and the median is the mean of the two middle values.

  • For example, for arr = [2,3,4], the median is 3.
  • For example, for arr = [2,3], the median is (2 + 3) / 2 = 2.5.

Implement the MedianFinder class:

  • MedianFinder() initializes the MedianFinder object.
  • void addNum(int num) adds the integer num from the data stream to the data structure.
  • double findMedian() returns the median of all elements so far. Answers within 10-5 of the actual answer will be accepted.

 

Example 1:

Input
["MedianFinder", "addNum", "addNum", "findMedian", "addNum", "findMedian"]
[[], [1], [2], [], [3], []]
Output
[null, null, null, 1.5, null, 2.0]

Explanation
MedianFinder medianFinder = new MedianFinder();
medianFinder.addNum(1);    // arr = [1]
medianFinder.addNum(2);    // arr = [1, 2]
medianFinder.findMedian(); // return 1.5 (i.e., (1 + 2) / 2)
medianFinder.addNum(3);    // arr[1, 2, 3]
medianFinder.findMedian(); // return 2.0

 

Constraints:

  • -105 <= num <= 105
  • There will be at least one element in the data structure before calling findMedian.
  • At most 5 * 104 calls will be made to addNum and findMedian.

Solution

class MedianFinder {
constructor() {
// invariant: minHeap.size() >= maxHeap.size()
this.maxHeap = new MaxPriorityQueue();
this.minHeap = new MinPriorityQueue();
this.evenSize = true; // is there an even number of elements
}

addNum(num) {
if (this.evenSize) {
this.maxHeap.enqueue(num);
this.minHeap.enqueue(this.maxHeap.dequeue().element);
} else {
this.minHeap.enqueue(num);
this.maxHeap.enqueue(this.minHeap.dequeue().element);
}
this.evenSize = !this.evenSize;
}

findMedian() {
if (this.minHeap.isEmpty()) {
return 0.0;
} else if (this.evenSize) {
return (this.minHeap.front().element + this.maxHeap.front().element) / 2.0;
} else {
return this.minHeap.front().element;
}
}
}

/**
* Your MedianFinder object will be instantiated and called as such:
* var obj = new MedianFinder()
* obj.addNum(num)
* var param_2 = obj.findMedian()
*/

We will use two heaps, a min-heap minHeap and a max-heap maxHeap; such that minHeap contains the larger half of all the values added, and maxHeap contains the smaller half of all the values added (ie. cycle between adding to the minHeap and maxHeap). Thus, the median is either the top value in minHeap, or the average top value of minHeap and maxHeap (depending on if there is an even or odd number of values). For the addNum method, in order to keep the previously mentioned property, there are two cases that needs to be considered:

  • If there is currently an even number of values (ie. minHeap.size() == maxHeap.size()), we want the value in range [maxHeap.top(), minHeap.top()) to be pushed into minHeap. Thus, to find this value (while also keeping the new value num in mind), we push num into maxHeap and push the top of it into minHeap.
  • If there is currently an odd number of values (ie. minHeap.size() > maxHeap.size()), we want the value in range (maxHeap.top(), minHeap.top()] to be pushed into maxHeap. Thus, to find this value (while also keeping the new value num in mind), we push num into minHeap and push the top of it into maxHeap.