题目描述
如何得到一个数据流中的中位数?如果从数据流中读出奇数个数值,那么中位数就是所有数值排序之后位于中间的数值。如果从数据流中读出偶数个数值,那么中位数就是所有数值排序之后中间两个数的平均值。我们使用Insert()方法读取数据流,使用GetMedian()方法获取当前读取数据的中位数。
代码
创建优先级队列维护大顶堆和小顶堆两个堆,并且小顶堆的值都大于大顶堆的值,2个堆个数的差值小于等于1,所以当插入个数为奇数时:大顶堆个数就比小顶堆多1,中位数就是大顶堆堆头;当插入个数为偶数时,使大顶堆个数跟小顶堆个数一样,中位数就是 2个堆堆头平均数。
注意:小顶堆的数一直比大顶堆的数要大
import java.util.*;
public class Solution {
//默认实现的是最小堆,数据从小到大排列
private PriorityQueue<Integer> low=new PriorityQueue<Integer>();
//最大堆要通过比较器Comparator实现
private PriorityQueue<Integer> high=new PriorityQueue<>((o1,o2)->o2-o1);
//(1) 写成return o1.compareTo(o2) 或者 return o1-o2表示升序
//(2) 写成return o2.compareTo(o1) 或者 return o2-o1表示降序);
//当前读入的数据个数
int count=0;
//读取数据流
public void Insert(Integer num) {
count++;
if((count%2)!=0){
//奇数的时候插入大顶堆
low.add(num);
high.add(low.poll());
}else{
/* N 为偶数的情况下插入到右半边。
* 因为右半边元素都要大于左半边,但是新插入的元素不一定比左半边元素来的大,
* 因此需要先将元素插入左半边,然后利用左半边为大顶堆的特点,取出堆顶元素即为最大元素,此时插入右半边 */
high.add(num);
low.add(high.poll());
}
}
//获取当前读取数据的中位数
public Double GetMedian() {
if(count==0){
return null;
}
if((count%2)!=0){
//奇数时中位数是大顶堆的头
return Double.valueOf(high.peek());
}else{
//偶数时中位数是小顶堆的头和大顶堆的头的和除2
return Double.valueOf((high.peek()+low.peek()))/2;
}
}
}