295.数据流的中位数
初见此题,笔者甚至怀疑这道题为何为“困难”题。笔者最初思路如下:
- 建立一个列表,将新增的数放入列表。
- 对列表进行排序,根据列表的长度,返回相应中位数。
class MedianFinder {
List<Integer> list = new ArrayList();
/** initialize your data structure here. */
public MedianFinder() {
}
public void addNum(int num) {
list.add(num);
}
public double findMedian() {
double ans = 0;
Collections.sort(list);
int num = list.size();
if(num % 2 == 0){
ans = ((double)(list.get(num/2) + (double)(list.get(num/2-1)))/2);
}
if(num == 1) ans = (double)list.get(0);
if(num % 2 != 0){
ans = (double)(list.get(num/2));
}
return ans;
}
}
/**
* Your MedianFinder object will be instantiated and called as such:
* MedianFinder obj = new MedianFinder();
* obj.addNum(num);
* double param_2 = obj.findMedian();
*/
很遗憾,超时了。
对于大量数据的读取,对列表进行排序明显时间需求过长。那么如何快速得到一个排好的序列,同时得到它的中位数呢?
答案就是——优先队列。
利用优先队列,我们能够在数据在加入时就对其进行排序,那么此时还存在一个问题:如何从优先队列中取得中位数呢?
我们能不能将这段序列放置于两个大小均等的优先队列中,一个为最小堆,一个为最大堆,那么我们能不能使这两个堆的堆顶为中位数呢?是可以的。
-
最小堆的堆顶元素需大于等于最大堆的堆顶元素。
-
两个堆的大小相差不能超过一。
-
为保证两个堆的大小相近,我们每次对数据进行增添时,需进行判断,确定进入哪个堆,同时保证上一个条件成立。
if(min.size() == max.size()){ min.offer(num); /* //保证最大堆的堆顶,小于最小堆 if(max.size() > 0 && max.peek() > num){ max.offer(min.poll()); } else{ max.offer(min.poll()); //此处判断则可以省略 } */ max.offer(min.poll()); } else{ max.offer(num); /* //保证最小堆的堆顶大于最大堆 if(min.size() > 0 && min.peek() < num){ min.offer(max.poll()); } else{ min.offer(max.poll()); //同上 } */ min.offer(max.poll());
-
保证了上述两个条件,那么最大堆中的元素均小于最小堆,所以最大堆的堆顶元素与最小堆的堆顶元素为该序列的中位数。
class MedianFinder {
private PriorityQueue<Integer> max;
private PriorityQueue<Integer> min;
/** initialize your data structure here. */
public MedianFinder() {
min = new PriorityQueue<>((x,y)->(x-y));
max = new PriorityQueue<>((x,y)->(y-x));
}
public void addNum(int num) {
if(min.size() == max.size()){
min.offer(num);
/*
//保证最大堆的堆顶,小于最小堆
if(max.size() > 0 && max.peek() > num){
max.offer(min.poll());
}
else{
max.offer(min.poll());
//此处判断则可以省略
}
*/
max.offer(min.poll());
}
else{
max.offer(num);
/*
//保证最小堆的堆顶大于最大堆
if(min.size() > 0 && min.peek() < num){
min.offer(max.poll());
}
else{
min.offer(max.poll());
//同上
}
*/
min.offer(max.poll());
}
}
public double findMedian() {
if(max.size() == min.size())
return ((double)max.peek()+(double)min.peek())/2;
return (double)max.peek();
}
}
/**
* Your MedianFinder object will be instantiated and called as such:
* MedianFinder obj = new MedianFinder();
* obj.addNum(num);
* double param_2 = obj.findMedian();
*/
成功通过。