数据流的中位数
难度:
标签:
题目描述
中位数是有序整数列表中的中间值。如果列表的大小是偶数,则没有中间值,中位数是两个中间值的平均值。
- 例如
arr = [2,3,4]
的中位数是3
。 - 例如
arr = [2,3]
的中位数是(2 + 3) / 2 = 2.5
。
实现 MedianFinder 类:
-
MedianFinder()
初始化MedianFinder
对象。 -
void addNum(int num)
将数据流中的整数num
添加到数据结构中。 -
double findMedian()
返回到目前为止所有元素的中位数。与实际答案相差10-5
以内的答案将被接受。
示例 1:
输入 ["MedianFinder", "addNum", "addNum", "findMedian", "addNum", "findMedian"] [[], [1], [2], [], [3], []] 输出 [null, null, null, 1.5, null, 2.0] 解释 MedianFinder medianFinder = new MedianFinder(); medianFinder.addNum(1); // arr = [1] medianFinder.addNum(2); // arr = [1, 2] medianFinder.findMedian(); // 返回 1.5 ((1 + 2) / 2) medianFinder.addNum(3); // arr[1, 2, 3] medianFinder.findMedian(); // return 2.0
提示:
-105 <= num <= 105
- 在调用
findMedian
之前,数据结构中至少有一个元素 - 最多
5 * 104
次调用addNum
和findMedian
代码结果
运行时间: 272 ms, 内存: 37.0 MB
/*
* 思路:我们使用Stream API来简化插入排序的操作。
* 1. 使用一个ArrayList来存储元素。
* 2. 每次插入新元素时,将其插入到正确的位置以保持有序。
* 3. 使用Stream来实现排序和中位数计算。
*/
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;
class MedianFinder {
private List<Integer> nums;
public MedianFinder() {
nums = new ArrayList<>();
}
public void addNum(int num) {
nums.add(num);
nums = nums.stream().sorted().collect(Collectors.toList());
}
public double findMedian() {
int size = nums.size();
if (size % 2 == 1) {
return nums.get(size / 2);
} else {
return (nums.get(size / 2 - 1) + nums.get(size / 2)) / 2.0;
}
}
}
解释
方法:
这个题解使用了两个堆来实现数据流的中位数查找。左边的堆是一个大顶堆,存储较小的一半数据;右边的堆是一个小顶堆,存储较大的一半数据。通过维护两个堆的大小平衡,并且保证左边堆的最大值小于等于右边堆的最小值,就可以在 O(1) 的时间复杂度内找到中位数。当数据总数为奇数时,中位数就是左边堆的堆顶元素;当数据总数为偶数时,中位数就是左右两个堆堆顶元素的平均值。
时间复杂度:
O(log n)
空间复杂度:
O(n)
代码细节讲解
🦆
为什么在`addNum`方法中,选择将数字添加到大顶堆或小顶堆时,需要比较数字与小顶堆的最小值和大顶堆的最大值?
▷🦆
在`addNum`方法中,当新加入的数字需要调整两个堆之间的元素时,具体的步骤是怎样的,能否详细说明这种调整的必要性和过程?
▷🦆
如何保证在整个过程中大顶堆的所有值始终小于或等于小顶堆的所有值?
▷🦆
在实现`findMedian`方法时,为什么在两个堆大小相等的情况下直接取两个堆顶元素的平均值就是中位数?
▷相关问题
滑动窗口中位数
中位数是有序序列最中间的那个数。如果序列的长度是偶数,则没有最中间的数;此时中位数是最中间的两个数的平均数。
例如:
[2,3,4]
,中位数是3
[2,3]
,中位数是(2 + 3) / 2 = 2.5
给你一个数组 nums,有一个长度为 k 的窗口从最左端滑动到最右端。窗口中有 k 个数,每次窗口向右移动 1 位。你的任务是找出每次窗口移动后得到的新窗口中元素的中位数,并输出由它们组成的数组。
示例:
给出 nums = [1,3,-1,-3,5,3,6,7]
,以及 k = 3。
窗口位置 中位数 --------------- ----- [1 3 -1] -3 5 3 6 7 1 1 [3 -1 -3] 5 3 6 7 -1 1 3 [-1 -3 5] 3 6 7 -1 1 3 -1 [-3 5 3] 6 7 3 1 3 -1 -3 [5 3 6] 7 5 1 3 -1 -3 5 [3 6 7] 6
因此,返回该滑动窗口的中位数数组 [1,-1,-1,3,5,6]
。
提示:
- 你可以假设
k
始终有效,即:k
始终小于等于输入的非空数组的元素个数。 - 与真实值误差在
10 ^ -5
以内的答案将被视作正确答案。