🧠 LeetCode 295. 数据流的中位数 | 思路总览
📝 题目要求:
设计一个支持以下操作的数据结构 MedianFinder
:
addNum(int num)
:添加一个整数到数据流中;findMedian()
:返回当前所有元素的中位数。
🎯 解题目标:
- 多次添加数字的同时,能快速计算中位数。
- 要求:
addNum
多次调用后,中位数查询时间必须高效。
🚧 问题分析:
中位数是“排序后位于中间位置的数”:
- 奇数个数:中间那个数;
- 偶数个数:中间两个数的平均值。
但:每次都排序整个数组代价太高,时间复杂度 O(n log n)。
💡 核心思路:两个堆维护中位数
使用两个堆分别维护数据流中的前一半和后一半:
✅ 数据结构设计:
堆名称 | 类型 | 内容描述 | 作用 |
---|---|---|---|
maxHeap | 大顶堆 | 较小的一半 | 提供前半部分最大值 |
minHeap | 小顶堆 | 较大的一半 | 提供后半部分最小值 |
✅ 不变量:
maxHeap.size()
≥minHeap.size()
(最多多 1 个)maxHeap.peek()
≤minHeap.peek()
(保证有序)
🔄 操作流程:
🔹 添加元素 addNum(num)
:
- 如果新元素 ≤ maxHeap 的堆顶 → 加入 maxHeap;
- 否则 → 加入 minHeap;
- 调整两个堆的大小,保持平衡:
- 如果 maxHeap 比 minHeap 多超过 1 个元素 → 把堆顶移到 minHeap;
- 如果 minHeap 比 maxHeap 多 → 把堆顶移到 maxHeap。
🔹 查找中位数 findMedian()
:
- 如果总数是奇数 → 中位数是
maxHeap.peek()
; - 如果是偶数 → 中位数是
(maxHeap.peek() + minHeap.peek()) / 2.0
。
⏱️ 时间复杂度分析:
操作 | 时间复杂度 |
---|---|
addNum | O(log n) |
findMedian | O(1) |
- 堆插入/删除是 log n;
- 获取堆顶是 O(1)。
✅ 总结:
使用两个堆(大顶堆 + 小顶堆)是一种非常经典的**“滑动中位数”或“实时中位数”维护技巧**,可以在动态数据流中快速计算中位数,非常适用于:
- 实时分析系统;
- 滑动窗口中位数;
- 面试高频数据结构设计题。
java solution
class MedianFinder {
//创建2个堆来动态维护中位数
private PriorityQueue<Integer> maxHeap;
private PriorityQueue<Integer> minHeap;
public MedianFinder() {
maxHeap = new PriorityQueue<>(Collec