题目链接
思路:双 大/小根堆
分析:看中位数的定义,排序后的中间的数
如果数据流的长度是奇数,那么中位数就是中间的那个数。
如果数据流的长度是偶数,那么中位数就是中间两个数的平均数。
问题一:每次判断长度然后去找中间的一个或者两个数的复杂度是O(n),并且每次插入数字,要排序好,时间复杂度O(nlogn),如果是n个数字,那么时间复杂度就是n2logn了。这个是需要解决的问题。
解决方案:我们需要的并不是整个有序,而仅仅是需要中间两个或者一个是有序,也就是说仅仅需要保证中间两个或者一个数字在有序的位置上,那么我们可以用两个堆,一个大顶堆,一个小顶堆,前半段是大顶堆,那么顶端就是中间的元素,后半段就是小顶堆,那么顶端就是中间的元素。
当取中位数的时候,通过判断前半段和后半段的个数就可以返回中位数。
代码:
class MedianFinder {
PriorityQueue<Integer> preQueue;
PriorityQueue<Integer> afterQueue;
/** initialize your data structure here. */
public MedianFinder() {
//大根堆
preQueue = new PriorityQueue<>((a,b)->{return b-a;});
//小根堆
afterQueue = new PriorityQueue<>((a,b)->{return a-b;});
}
public void addNum(int num) {
if(preQueue.isEmpty() || preQueue.peek()>num){
//比前半段最大的小,说明num肯定在前半段
preQueue.add(num);
//如果前半段比后半段多两个,那么需要把前半段的元素移动一个到后半段
if(afterQueue.size()+1<preQueue.size()){
afterQueue.add(preQueue.poll());
}
}else{
//否则肯定在后半段
afterQueue.add(num);
//如果后半段比前半段多两个,那么需要把后半段的元素移动一个到前半段
if(preQueue.size()+1 < afterQueue.size()){
preQueue.add(afterQueue.poll());
}
}
}
public double findMedian() {
//如果前半段和后半段个数一样,那么就是偶数个
if(preQueue.size()==afterQueue.size()){
return ((preQueue.peek()+afterQueue.peek()) * 1.0) / 2;
}
//否则,多的那一边的多一个数多的就是那个顶元素就是中位数
if(preQueue.size()>afterQueue.size()){
return preQueue.peek();
}else{
return afterQueue.peek();
}
}
}
好好学习。
不打扰是我的温柔。