295.数据流的中位数
OOP, heap, https://leetcode.cn/problems/find-median-from-data-stream/
中位数是有序整数列表中的中间值。如果列表的大小是偶数,则没有中间值,中位数是两个中间值的平均值。
- 例如
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提示:
-10^5 <= num <= 10^5- 在调用
findMedian之前,数据结构中至少有一个元素 - 最多
5 * 10^4次调用addNum和findMedian
解法:最大堆 + 最小堆
要高效地维护数据流的 中位数,我们可以使用 两个堆:
- 最大堆(left_heap) 存储较小的一半元素(取最大值)
- 最小堆(right_heap) 存储较大的一半元素(取最小值)
这样:
- 如果元素个数为奇数,中位数是 最大堆的堆顶。
- 如果元素个数为偶数,中位数是 最大堆的堆顶 和 最小堆的堆顶的均值。
Python 代码
python
import heapq
class MedianFinder:
def __init__(self):
# 最大堆(存储较小的一半,取反存入以模拟最大堆)
self.left_heap = []
# 最小堆(存储较大的一半)
self.right_heap = []
def addNum(self, num: int) -> None:
# 先将 num 放入最大堆(但因为 Python 没有最大堆,我们存入负数来模拟)
heapq.heappush(self.left_heap, -num)
# 确保最大堆的最大值 ≤ 最小堆的最小值
if self.left_heap and self.right_heap and (-self.left_heap[0] > self.right_heap[0]):
heapq.heappush(self.right_heap, -heapq.heappop(self.left_heap))
# 平衡两个堆的大小,使得最大堆的元素个数 ≥ 最小堆的元素个数
if len(self.left_heap) > len(self.right_heap) + 1:
heapq.heappush(self.right_heap, -heapq.heappop(self.left_heap))
elif len(self.right_heap) > len(self.left_heap):
heapq.heappush(self.left_heap, -heapq.heappop(self.right_heap))
def findMedian(self) -> float:
# 如果元素个数是奇数,中位数是最大堆的堆顶
if len(self.left_heap) > len(self.right_heap):
return -self.left_heap[0]
# 如果元素个数是偶数,中位数是两个堆顶的平均值
return (-self.left_heap[0] + self.right_heap[0]) / 2.0最大堆 + 最小堆维护数据流的中位数O(log n) 插入,O(1) 查询,适用于 大数据流
Python heapq 默认最小堆,用负数模拟最大堆