题目要求:如何得到一个数据流中的中位数?如果从数据流中读出奇数个数值,那么中位数就是所有数值排序之后位于中间的数值。如果从数据流中读出偶数个数值,那么中位数就是所有数值排序之后中间两个数的平均值。我们使用Insert()方法读取数据流,使用GetMedian()方法获取当前读取数据的中位数。
用两个堆保存数据,保持两个堆的数据保持平衡(元素个数相差不超过1)
大顶堆存放的数据要比小顶堆的数据小
当两个堆中元素为奇数个,将新加入元素加入到大顶堆,如果要加入的数据,比小顶堆的最小元素大,先将该元素插入小顶堆,然后将小顶堆的最小元素插入到大顶堆。
当两个堆中元素为偶数个,将新加入元素加入到小顶堆,如果要加入的数据,比大顶堆的最大元素小,先将该元素插入大顶堆,然后将大顶堆的最大元素插入到小顶堆。
1 import java.util.Comparator; 2 import java.util.PriorityQueue; 3 public class Solution { 4 //全局变量count用来统计加入了多少个数 5 int count=0; 6 PriorityQueue<Integer> minHeap = new PriorityQueue<Integer>(); 7 PriorityQueue<Integer> maxHeap = new PriorityQueue<Integer>(11, new Comparator<Integer>() { 8 @Override 9 public int compare(Integer o1, Integer o2) { 10 //PriorityQueue默认是小顶堆,实现大顶堆,需要反转默认排序器 11 return o2.compareTo(o1); 12 } 13 }); 14 public void Insert(Integer num) { 15 count++; 16 //每次插入小顶堆的是当前大顶堆中最大的数 17 //每次插入大顶堆的是当前小顶堆中最小的数 18 //这样保证小顶堆中的数永远大于等于大顶堆中的数 19 //中位数就可以方便地从两者的根结点中获取了 20 if( (count & 1) == 0) { // 判断偶数的高效写法 21 if (!maxHeap.isEmpty() && num < maxHeap.peek()) { 22 maxHeap.offer(num); 23 num = maxHeap.poll(); 24 } 25 minHeap.offer(num); 26 } else { //奇数情况 27 if (!minHeap.isEmpty() && num > minHeap.peek()) { 28 minHeap.offer(num); 29 num = minHeap.poll(); 30 } 31 maxHeap.offer(num); 32 } 33 } 34 public Double GetMedian() { 35 if(count==0) 36 throw new RuntimeException("no available number!"); 37 double result; 38 //总数为奇数时,大顶堆堆顶就是中位数 39 if((count&1)==1) 40 result=maxHeap.peek(); 41 else 42 result=(minHeap.peek()+maxHeap.peek())/2.0; 43 return result; 44 } 45 }