题目
如何得到一个数据流中的中位数?如果从数据流中读出奇数个数值,那么中位数就是所有数值排序之后位于中间的数值。
如果从数据流中读出偶数个数值,那么中位数就是所有数值排序之后中间两个数的平均值。
思路
采用大根堆+小根堆结合的方法,且满足:
两个堆中的数据数目差不能超过1,这样可以使中位数只会出现在两个堆的交接处;
大顶堆的所有数据都小于小顶堆,这样就满足了排序要求。
1、插入的思路:
1)若已读取的个数为偶数(包括0)时,两个堆的数目已经相同,将新读取的数插入到小顶堆中,从而实现小顶堆的个数多一。但是,如果新读取的数字比大顶堆中最大的数字还小,就不能直接插入到小顶堆中了 ,此时必须将新数字插入到大顶堆中,而将大顶堆中的最大数字插入到小顶堆中,从而实现小顶堆的个数多一。
2)若已读取的个数为奇数时,小顶堆的个数多一,所以要将新读取数字插入到大顶堆中,此时方法与上面类似。
2、取中位数的思路:
当总个数为偶数时,使两个堆的数目相同,则中位数=大顶堆的最大数字与小顶堆的最小数字的平均值;
而总个数为奇数时,使小顶堆的个数比大顶堆多一,则中位数=小顶堆的最小数字。
测试算例
1.功能测试(读入奇/偶数个数字)
2.边界值测试(读入0个、1个、2个数字)
总结
1、使用Java提供的新的数据接口PriorityQueue,其默认内部是自然排序,结果为小顶堆,也可以自定义排序器,比如下面反转比较,完成大顶堆。
PriorityQueue<Integer> maxHeap = new PriorityQueue<Integer>(11,new Comparator<Integer>(){ //大顶堆,容量11
public int compare(Integer i1,Integer i2){
return i2-i1;
}
});
2、优先队列常见方法:
1)offer:往队列中加元素,在插入失败时抛出异常。
add:往队列中加入元素,插入失败会返回false。
2)element:获取但不删除队首元素,操作失败会抛出异常。
peek:获取但不删除队首元素,操作失败会返回null。
3)remove:获取并删除队首元素,区别是当方法失败时会抛出异常;
poll:该方法返回队列头部的元素,如果队列为空,则返回NULL。
4)队列求大小为.size()