题目:
如何得到一个数据流中的中位数?如果从数据流中读出奇数个数值,那么中位数就是所有数值排序之后位于中间的数值。如果从数据流中读出偶数个数值,那么中位数就是所有数值排序之后中间两个数的平均值。
思路:
定义两个优先队列即PriorityQueue,一个作为大顶堆另一个作为小顶堆。这里需要说明下自己最近对于堆的理解,以前一直以为堆和集合一样是一群无序的数据,但是自从做了这道题之后开始有了对堆更为清晰的了解,发现自己之前所知道的完全是错的。大家可以想想堆排序。堆排序是依靠二叉树找到最大或者最小的那个数之后不断的调整二叉树来完成排序,注意这里所使用到的二叉树是完全二叉树。堆就是完全二叉树的数据结构,这也是堆排序名字的由来。自己之前简单的想到了使用ArrayList来存储数据最后判断列表是奇数还是偶数来求出中位数。但参考了各位前辈的做法发现此题可以依靠PriorityQueue更加灵活的解出来。初始化两个堆之后,下一步的任务是将其中的一个堆作为大顶堆。这里需要说明PriorityQueue在默认情况下是按照小顶堆进行排序的,所以在这里需要使用Comparator类。
new Comparator<Integer>() {
@Override
public int compare(Integer o1, Integer o2) {
// TODO Auto-generated method stub
return o2-o1;
}
});
再定义一个索引(起到计数器的作用),判断每次插入数值的实收队列的总长度为偶数还是奇数。
当插入数据时列表的长度为偶数时,必然之前的小顶堆为奇数,大顶堆为偶数。先将新来的数据在大顶堆中进行一次比较,之后将大顶堆中最大的数拿出给到小顶堆。
当插入的数据是的整体列表长度为奇数时,先在小顶堆中进行一次比较,将最小的数取出放到大顶堆中。
如果列表长度为偶数,取出两个堆中的头部元素,相当于将这一串数分为两部分,取出前一部分最大的和后一部分最小的,二者相加求平均值即中位数。
如果列表长度为奇数,则取出小顶堆的头部数据。
解题代码:
public class Solution{
/**
* 定义两个优先队列用于存储数据
* 优先级队列在默认的情况下按照从小到大排序
*/
PriorityQueue<Integer>minQueue = new PriorityQueue<Integer>();
PriorityQueue<Integer>maxQueue = new PriorityQueue<Integer>(15,new Comparator<Integer>() {
@Override
public int compare(Integer o1, Integer o2) {
// TODO Auto-generated method stub
return o2-o1;
}
});
//定义计数器用于统计插入的数据的数量
private int index = 0;
public void Insert(Integer num) {
int hub = 0;
if(index%2 == 0){
//offer方法用于将制定的元素插入队列当中
maxQueue.offer(num);
//poll方法获取并移除队列的头
hub = maxQueue.poll();
minQueue.offer(hub);
}else{
minQueue.offer(num);
hub = minQueue.remove();
maxQueue.offer(hub);
}
index++;
}
//获取中位数
public Double GetMedian() {
Iterator<Integer>ite = minQueue.iterator();
while(ite.hasNext()){
System.out.println(ite.next());
}
//判断为空的情况
if(index == 0)
return null;
//队列总数为偶数的情况
if(index%2 == 0){
Double num1 = Double.valueOf(String.valueOf(maxQueue.peek()));
Double num2 = Double.valueOf(String.valueOf(minQueue.peek()));
return (num1+num2)/2;
}
//队列总数为奇数的情况
if(index%2 == 1){
return Double.valueOf(String.valueOf(minQueue.peek()));
}
return 0.0;
}
}