主题
比如现在有
回顾一下百科中关于中位数的定义:
中位数……可将数值集合划分为相等的两部分。
中位数把这
随着
- 保证
的大小和 的大小尽量相等。规定:在有奇数个数时, 比 多 个数。 - 保证
的所有元素都小于等于 的所有元素。
只要时时刻刻满足以上两个要求(满足中位数的定义),我们就可以用
分类讨论:
- 如果当前
的大小和 的大小相等: - 如果添加的数字
比较大,比如添加 ,那么把 加到 中。现在 比 少 个数,不符合前文的规定,所以必须把 的最小值从 中去掉,添加到 中。如此操作后,可以保证 的所有元素都小于等于 的所有元素。 - 如果添加的数字
比较小,比如添加 ,那么把 加到 中。 - 这两种情况可以合并:无论
是大是小,都可以先把 加到 中,然后把 的最小值从 中去掉,并添加到 中。
- 如果添加的数字
- 如果当前
比 多 个数: - 如果添加的数字
比较大,比如添加 ,那么把 加到 中。 - 如果添加的数字
比较小,比如添加 ,那么把 加到 中。现在 比 多 个数,不符合前文的规定,所以必须把 的最大值从 中去掉,添加到 中。如此操作后,可以保证 的所有元素都小于等于 的所有元素。 - 这两种情况可以合并:无论
是大是小,都可以先把 加到 中,然后把 的最大值从 中去掉,并添加到 中。
- 如果添加的数字
最后,我们需要什么样的数据结构?这个数据结构要能高效地执行如下操作:
- 添加元素。
- 找到最大(小)值。
- 删除最大(小)值。
这个数据结构是堆。
- 如果当前有奇数个元素,中位数是
的堆顶。 - 如果当前有偶数个元素,中位数是
的堆顶和 的堆顶的平均值。
python
class MedianFinder:
def __init__(self):
self.left = [] # 最大堆
self.right = [] # 最小堆
def addNum(self, num: int) -> None:
if len(self.left) == len(self.right):
heappush_max(self.left, heappushpop(self.right, num))
else:
heappush(self.right, heappushpop_max(self.left, num))
def findMedian(self) -> float:
if len(self.left) > len(self.right):
return self.left[0]
return (self.left[0] + self.right[0]) / 2cpp
// C++ 版待补充cpp
class MedianFinder {
priority_queue<int> left; // 最大堆
priority_queue<int, vector<int>, greater<>> right; // 最小堆
public:
void addNum(int num) {
if (left.size() == right.size()) {
right.push(num);
left.push(right.top());
right.pop();
} else {
left.push(num);
right.push(left.top());
left.pop();
}
}
double findMedian() {
if (left.size() > right.size()) {
return left.top();
}
return (left.top() + right.top()) / 2.0;
}
};复杂度分析
- 时间复杂度:初始化和
都是 , 是 ,其中 是 的调用次数。每次操作堆需要 的时间。 - 空间复杂度:
。
思考题
如果还有删除操作(删除数据流中的任意元素),要求仍然用堆实现,要怎么做?
见 480. 滑动窗口中位数。
专题训练
见下面数据结构题单的「§5.7 对顶堆」。
分类题单
- 滑动窗口与双指针(定长/不定长/单序列/双序列/三指针/分组循环)
- 二分算法(二分答案/最小化最大值/最大化最小值/第K小)
- 单调栈(基础/矩形面积/贡献法/最小字典序)
- 网格图(DFS/BFS/综合应用)
- 位运算(基础/性质/拆位/试填/恒等式/思维)
- 图论算法(DFS/BFS/拓扑排序/基环树/最短路/最小生成树/网络流)
- 动态规划(入门/背包/划分/状态机/区间/状压/数位/数据结构优化/树形/博弈/概率期望)
- 常用数据结构(前缀和/差分/栈/队列/堆/字典树/并查集/树状数组/线段树)
- 数学算法(数论/组合/概率期望/博弈/计算几何/随机算法)
- 贪心与思维(基本贪心策略/反悔/区间/字典序/数学/思维/脑筋急转弯/构造)
- 链表、树与回溯(前后指针/快慢指针/DFS/BFS/直径/LCA)
- 字符串(KMP/Z函数/Manacher/字符串哈希/AC自动机/后缀数组/子序列自动机)
欢迎关注 B站@灵茶山艾府