题目
如何得到一个数据流中的中位数?
如果从数据流中读出奇数个数值,那么中位数就是所有数值排序之后位于中间的数值。如果从数据流中读出偶数个数值,那么中位数就是所有数值排序之后中间两个数的平均值。我们使用Insert()
方法读取数据流,使用GetMedian()
方法获取当前读取数据的中位数。
数据范围:数据流中数个数满足 ,大小满足
进阶: 空间复杂度 , 时间复杂度
示例1
输入:[5,2,3,4,1,6,7,0,8] 返回值:“5.00 3.50 3.00 3.50 3.00 3.50 4.00 3.50 4.00 “ 说明:数据流里面不断吐出的是5,2,3…,则得到的平均数分别为5,(5+2)/2,3…
解题思路:优先队列 / 堆
建立一个 大顶堆 和 小顶堆
,各保存列表的一半元素,且规定:
保存 较小 的一半,长度为
(
为偶数)或
(
为奇数)
保存 较大 的一半,长度为
(
为偶数)或
(
为奇数)
随后,中位数可仅根据 和
的堆顶元素计算得到。
添加算法流程:设总数
当
(即
为 偶数):需向
添加一个元素。
实现方法:将新元素
num
插入至,再将
堆顶元素插入至
;
当
(即
为 奇数):需向
添加一个元素。
实现方法:将新元素
num
插入至,再将
堆顶元素插入至
;
查找算法流程:设总数
- 当
(即
为 偶数):则中位数为 (
的堆顶元素 +
的堆顶元素 )/2。
- 当
(即
为 奇数):则中位数为
的堆顶元素
复杂度分析
时间复杂度: ,其中
是数组长度。
- 查找中位数 :获取堆顶元素使用 时间;
- 添加数字  : 堆的插入和弹出操作使用  时间。
空间复杂度: : 其中
为数据流中的元素数量
官方代码
class MedianFinder {
Queue<Integer> A, B;
public MedianFinder() {
A = new PriorityQueue<>((o1, o2) -> {o2 - o1}); // 大顶堆,保存较小的一半
B = new PriorityQueue<>(); // 小顶堆,保存较大的一半
}
public void addNum(int num) {
// 奇数,向B加
if(A.size() != B.size()) {
A.add(num);
B.add(A.poll());
} else { // 偶数,向A加
B.add(num);
A.add(B.poll());
}
}
public double findMedian() {
return A.size() != B.size() ? A.peek() : (A.peek() + B.peek()) / 2.0;
}
}
我的代码
public class Solution {
// A 存数字小的区域的大根堆
PriorityQueue<Integer> ASmallMax=new PriorityQueue<>((o1,o2)->{
return o2-o1;
});
// B 存数字大的区域的小根堆
PriorityQueue<Integer> BBigMin=new PriorityQueue<>((o1,o2)->{
return o1-o2;
});
public void Insert(Integer num) {
// 奇数,向B中添加
if(ASmallMax.size()>BBigMin.size()){
ASmallMax.add(num);
BBigMin.add(ASmallMax.poll());
}else{ // 偶数,向A中添加
BBigMin.add(num);
ASmallMax.add(BBigMin.poll());
}
return ;
}
public Double GetMedian() {
// 奇数
if(ASmallMax.size()>BBigMin.size()){
return (double)ASmallMax.peek();
}
else{
return (ASmallMax.peek()+BBigMin.peek())/2.0;
}
}
}