[LeetCode]数据流
1.数据流的中位数
中位数是有序列表中间的数。如果列表长度是偶数,中位数则是中间两个数的平均值。例如,
[2,3,4] 的中位数是 3
[2,3] 的中位数是 (2 + 3) / 2 = 2.5
设计一个支持以下两种操作的数据结构:
void addNum(int num) - 从数据流中添加一个整数到数据结构中。
double findMedian() - 返回目前所有元素的中位数。
示例:
addNum(1)
addNum(2)
findMedian() -> 1.5
addNum(3)
findMedian() -> 2
进阶:
如果数据流中所有整数都在 0 到 100 范围内,你将如何优化你的算法?
如果数据流中 99% 的整数都在 0 到 100 范围内,你将如何优化你的算法?
第一种思路:用一个数组存储所有数据,每次添加一个新的数据的时候,对数组排序一次。addNum时间复杂度为O(n)+O(nlogn),findMedian查找复杂度为O(1)。这种思路是批式处理思想的体现,多见于数据分析过程中,容易想到但是冗余较多。体现在两个地方:
第一:数组添加数据的时候append操作
第二:每次更新数组后都要排序
既然findMedian要求一个有序数组就OK了,那么在数据流过程中,可以维护一个有序的数据结构。那么addNum的时间复杂度可以变为O(nlgn)。这种思路是流式处理思想的体现。
第三:结合数组和中位数的特点,空间换时间,addNum的时间复杂度变为O(lgn)。这种思路也可以理解为对问题进一步分解,将一个原来一步解决的问题,分为两步,但是第二步的时间复杂度较低,这是一个分支优化。这里是一个非常干净的方案。
2.数据流中的移动平均值
给定一个数据流输入 a1,a2,…,an,…,和固定窗口大小为n,计算固定窗口内的数据的平均值。
测试用例如下:
m = MovingAverage(3)
print(m.next(1))
print(m.next(10))
print(m.next(3))
print(m.next(5))
print(m.next(3))
print(m.next(5))
预期输出:
1.0
5.5
4.666666666666667
6.0
3.6666666666666665
4.333333333333333
基本思路:选用一个合适的数据结构用来模拟数据流。如下:
class MovingAverage:
def __init__(self, size: int):
from collections import deque
#通过设定deque的最大值模拟固定窗口
self.deque = deque(maxlen=size)
def next(self, val:int)->float:
#通过appendleft模拟数据流动
self.deque.appendleft(val)
return sum(self.deque) / len(self.deque)
deque是一种直接使用比较清爽的结构,用其他数据结构也可以实现。
3.蓄水池采样
给定一个数据流输入 a1,a2,…,an,…,用等概率的方式采样m个数据。
4.将数据流变为多个不相交区间
给定一个非负整数的数据流输入 a1,a2,…,an,…,将到目前为止看到的数字总结为不相交的区间列表。
例如,假设数据流中的整数为 1,3,7,2,6,…,每次的总结为:
[1, 1]
[1, 1], [3, 3]
[1, 1], [3, 3], [7, 7]
[1, 3], [7, 7]
[1, 3], [6, 7]
进阶: 如果有很多合并,并且与数据流的大小相比,不相交区间的数量很小,该怎么办?