Find Median from Data Stream
Published:
问题描述
创建一个数据结构,可以存储一个可以随时间动态变化大小的整数列表,并在常数时间内 O(1)
找到这个动态增长列表的中位数。
实现一个名为 MedianOfStream
的结构体
该结构体应该提供以下功能:
Init():初始化数据结构,设置必要的组件来管理整数流,例如创建最大堆和最小堆。
Insert Num(num):向数据结构中添加一个整数
num
。Find Median():找到当前所有元素的中位数。如果元素数量为偶数,返回中间两个值的平均值。
约束条件
- 整数
num
满足以下约束:-10^5 ≤ num ≤ 10^5
。 - 在计算中位数之前,数据结构中至少有一个元素。
- 最多会调用 500 次计算中位数的函数。
求中位数步骤
- 首先:是对数字排序
- 然后:如果数组是奇数的话,中位数就是中间的值
(start+end)/2
- 最后:如果是偶数的话,中位数是中间两个数加起来除以
(mid+mid+1)/2
那我们直接对数组排序,然后就这样就不就可以了吗?
得以O(1)
常数时间内查找到才可以,但是步骤还是那个步骤,只是sort
放在了heap
里面利用maxheap
和miniheap
插入元素上浮和下移的性质就可以做到排序。
那应该怎么样插入数据呢?
我们让最小堆放素组中较大的一半元素,最大堆放较小的。
使用堆的思路
- 两个堆的概念:
- 最大堆 (
maxHeap
):存储较小的一半数据,堆顶是这一部分的最大值。 - 最小堆 (
minHeap
):存储较大的一半数据,堆顶是这一部分的最小值。
- 最大堆 (
- 堆的平衡:
- 通过保持两个堆的大小差不超过1,可以保证中位数始终处于堆顶或堆顶的平均值中。
- 如果数据流中的元素个数为奇数,中位数为元素较多的那个堆的堆顶元素。
- 如果数据流中的元素个数为偶数,中位数为两个堆的堆顶元素的平均值。
插入数据的具体步骤
- 判断插入位置:
- 如果新元素小于或等于最大堆的堆顶元素,将其插入最大堆。
- 否则,将其插入最小堆。
- 调整堆的大小:
- 插入元素后,检查两个堆的大小。如果某个堆的元素数量超过另一个堆超过1个,则需要将该堆的堆顶元素移动到另一个堆中,以保持平衡。
- 例如,如果
maxHeap
的元素比minHeap
多2个,则将maxHeap
的堆顶元素移动到minHeap
。
- 查找中位数:
- 当两个堆的大小相等时,中位数是两个堆顶元素的平均值。
- 当两个堆的大小不相等时,中位数是元素较多的那个堆的堆顶元素。
代码如下:
插入数据示例
假设插入的顺序是 [5, 15, 1, 3]
,插入过程如下:
- 插入 5:
maxHeap
:[5]
minHeap
:[]
- 中位数:5
- 插入 15:
maxHeap
:[5]
minHeap
:[15]
- 中位数:
(5 + 15) / 2 = 10
- 插入 1:
maxHeap
:[5, 1]
minHeap
:[15]
- 中位数:5
- 插入 3:
- 平衡堆:
[3, 1]
,[5, 15]
- 中位数:
(3 + 5) / 2 = 4
- 平衡堆:
总结
通过最大堆和最小堆的配合,可以有效地管理数据流,并在 O(1)
时间内查找中位数。插入操作的复杂度为 O(log n)
,但查找中位数的操作只需要查看堆顶元素,时间复杂度为 O(1)
。