Skip to content

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 次调用 addNumfindMedian

解法:最大堆 + 最小堆

要高效地维护数据流的 中位数,我们可以使用 两个堆

  1. 最大堆(left_heap) 存储较小的一半元素(取最大值)
  2. 最小堆(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 默认最小堆,用负数模拟最大堆