题目:
如何得到一个数据流中的中位数?如果从数据流中读出奇数个数值,那么中位数就是所有数值排序之后位于中间的数值。如果从数据流中读出偶数个数值,那么中位数就是所有数值排序之后中间两个数的平均值。我们使用Insert()方法读取数据流,使用GetMedian()方法获取当前读取数据的中位数。
分析:
我们可以想象出一个排序数组的中位数可以将数组分成两部分,那么我们利用最大堆和最小堆来分别存储这两部分,且如果要求中位数时,可以通过直接返回堆顶元素,在O(1)的时间内求解。
要保证最大堆的所有元素小于等于最小堆的元素,在插入元素的时候通过已插入数字数目来判断:
如果插入数目是偶数,就将元素插入最大堆,然后再将最大堆中堆顶元素取出,插入最小堆中。
如果插入数目是奇数,就将元素插入最小堆,然后再将最小堆中堆顶元素取出,插入最大堆中。
这样做就可以保证最大堆的所有元素小于等于最小堆的元素。
然后如果数目是奇数时,中位数等于最小堆的堆顶元素,为偶数时,中位数等于最小堆和最大堆的堆顶元素的平均值。
以[5,2,3,4,1,6,7,0,8]为例:
插入元素 | 最小堆 | 最大堆 | 中位数 |
5 | [5] | [] | 5 |
2 | [5] | [2] | 3.5 |
3 | [3,5] | [2] | 3 |
4 | [4,5] | [3,2] | 3.5 |
1 | [3,4,5] | [2,1] | 3 |
6 | [4,5,6] | [3,2,1] | 3.5 |
7 | [4,5,6,7] | [3,2,1] | 4 |
0 | [4,5,6,7] | [3,2,1,0] | 3.5 |
8 | [4,5,6,7,8] | [3,2,1,0] | 4 |
程序:
C++
class Solution { public: void Insert(int num) { if(c % 2 == 0){ maxHeap.push(num); minHeap.push(maxHeap.top()); maxHeap.pop(); } else{ minHeap.push(num); maxHeap.push(minHeap.top()); minHeap.pop(); } c++; } double GetMedian() { if(c % 2 == 0){ return (double)(maxHeap.top() + minHeap.top()) / 2; } else{ return (double)minHeap.top(); } } private: priority_queue<int, vector<int>, less<int> > maxHeap; priority_queue<int, vector<int>, greater<int> > minHeap; int c = 0; };
Java
import java.util.PriorityQueue; import java.util.Comparator; public class Solution { public void Insert(Integer num) { if(count % 2 == 0){ maxHeap.offer(num); minHeap.offer(maxHeap.poll()); }else{ minHeap.offer(num); maxHeap.offer(minHeap.poll()); } count++; } public Double GetMedian() { if(count % 2 == 0){ return new Double(minHeap.peek() + maxHeap.peek()) / 2; }else{ return new Double(minHeap.peek()); } } private PriorityQueue<Integer> minHeap = new PriorityQueue<Integer>(); private PriorityQueue<Integer> maxHeap = new PriorityQueue<Integer>(11, new Comparator<Integer>() { @Override public int compare(Integer o1, Integer o2) { return o2 - o1; } }); private int count = 0; }