Problem:
如何得到一个数据流中的中位数?如果从数据流中读出奇数个数值,那么中位数就是所有数值排序之后位于中间的数值。如果从数据流中读出偶数个数值,那么中位数就是所有数值排序之后中间两个数的平均值。我们使用Insert()方法读取数据流,使用GetMedian()方法获取当前读取数据的中位数。
Intuition:
首先我们需要保证左右两个堆的大小相等,大数存在最小堆里,小数存在最大堆里,这样我们就可以通过堆顶来算中位数。
由于我们需要知道的是中位数,所以自然要求最小堆和最大堆的元素数量相等,那么我们的做法是一旦数目为偶数,就让小顶堆的元素数量加1(先插入大顶堆
,但是又从大顶堆中移除一个到小顶堆,所以大顶堆的元素数量不变)。一旦为奇数,就反着来。
那么这个时候就有疑问了,为什么要让元素先进最小(大)堆,再进入最大(小)堆呢?
因为我们算中位数的时候有一个前提,即所有最大堆的元素都小于等于最小堆,那么我们把一个元素插入到了任意一边的堆中都可能影响当前的平衡,这个时候
就需要移动堆顶的元素来恢复平衡
当数目为偶数的时候,将这个值插入大顶堆中,再将大顶堆中根节点(即最大值)插入到小顶堆中;
当数目为奇数的时候,将这个值插入小顶堆中,再讲小顶堆中根节点(即最小值)插入到大顶堆中;
Solution:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42
| public class CodingInterview_041 { private PriorityQueue<Integer> minHeap = new PriorityQueue<>(); private PriorityQueue<Integer> maxHeap = new PriorityQueue<Integer>(15, new Comparator<Integer>() { @Override public int compare(Integer o1, Integer o2) { return o2 - o1; } });
int count = 0; public void Insert(Integer num) { if(count % 2 == 0){ maxHeap.offer(num); int max = maxHeap.poll(); minHeap.offer(max); }else{ minHeap.offer(num); int min = minHeap.poll(); maxHeap.offer(min); } count++; }
public Double GetMedian() { if(count % 2 == 0){ return new Double(minHeap.peek() + maxHeap.peek())/2; }else{ return new Double(minHeap.peek()); } } }
|