思路:
使用2个优先队列,一个存储较大的一半,另一个存储较小的一半
如果是奇数,就返回小顶堆(存储较大一半)的堆顶元素
如果是偶数,就返回两堆堆顶元素/2.0的值
代码:
class MedianFinder {
Queue<Integer> A,B;
/** initialize your data structure here. */
public MedianFinder() {
A=new PriorityQueue<>();//小顶堆(存储较大的一半)
B=new PriorityQueue<>((a,b)->(b-a));//大顶堆(存储较小的一半)
}
public void addNum(int num) {
if(A.size()==B.size()){
//向A中添加元素,必须先将此元素添加到B中确认是否属于较小的一半
B.add(num);
A.add(B.poll());
}
else{
//向B中添加元素,必须先将此元素添加到A中确认是否属于较大的一方
A.add(num);
B.add(A.poll());
}
}
public double findMedian() {
if(A.size()==B.size()){
//偶数,取中间2数之和/2
//因为返回的是double类型,所以要/2.0
return (A.peek()+B.peek())/2.0;
}
else{
//奇数,取A的顶端元素
return A.peek();
}
}
}
/**
* Your MedianFinder object will be instantiated and called as such:
* MedianFinder obj = new MedianFinder();
* obj.addNum(num);
* double param_2 = obj.findMedian();
*/
分解:
1)小顶堆:堆顶是整个堆中最小的元素,升序,PriorityQueue默认是升序(小顶堆)
大顶堆:堆顶是整个堆中最大的元素,降序,要重写比较器
2)整个过程:
i)如果A、B的元素个数不相等,就往A中添加元素。但这个元素可能是属于较小一半的,所以要将此数加到B中,再从B的堆顶弹出较大的数,加入到A中
ii)如果A、B的元素个数相等,就往B中添加元素。但这个歌元素可能是属于较大一半的,所以要将此数加到A中,再从A的堆顶弹出较小的数,加入到B中
求中位数:
iii)如果A、B总和为偶数,则分别弹出A、B的堆顶元素,相加后/2.0
iv)如果A、B总和为奇数,返回A的堆顶元素即可
3)因为中位数要求是double类型,所以要/2.0而不是2.否则答案不正确
复杂度分析:
时间复杂度:O(logN)
i)查找中位数:O(1),只查找堆顶元素即可
ii)堆的插入,弹出:O(logN)
空间复杂度:O(N) A、B最多同时存储N个元素