295. 数据流的中位数

295. 数据流的中位数

原始题目链接:https://leetcode-cn.com/problems/find-median-from-data-stream/

如何得到一个数据流中的中位数?如果从数据流中读出奇数个数值,那么中位数就是所有数值排序之后位于中间的数值。如果从数据流中读出偶数个数值,那么中位数就是所有数值排序之后中间两个数的平均值。

例如,

[2,3,4] 的中位数是 3

[2,3] 的中位数是 (2 + 3) / 2 = 2.5

设计一个支持以下两种操作的数据结构:

void addNum(int num) - 从数据流中添加一个整数到数据结构中。
double findMedian() - 返回目前所有元素的中位数。

示例 1:
输入
[“MedianFinder”,“addNum”,“addNum”,“findMedian”,“addNum”,“findMedian”]
[[],[1],[2],[],[3],[]]
输出:[null,null,null,1.50000,null,2.00000]

示例 2:
输入:

[“MedianFinder”,“addNum”,“findMedian”,“addNum”,“findMedian”]
[[],[2],[],[3],[]]
输出:[null,null,2.00000,null,2.50000]

解题思路:

给定一长度为 NN 的无序数组,其中位数的计算方法:首先对数组执行排序(O(NlogN)时间),然后返回中间元素即可( O(1)时间)。借助堆可以进一步优化时间复杂度,建立一个 小顶堆A和大顶堆B,各保存列表的一半元素,且规定: A保存较大的一半数据,长度为N/2,N为偶数或者(N+1)/2,N为奇数B保存较小的一版数据,长度为N/2,N为偶数或者(N+1)/2,N为奇数> 这样中位数可以根据堆顶元素计算获得,注意python中的堆是小根堆,大根堆可以根据数据取反来实现,具体见代码实现。

在这里插入图片描述
addNum(num) 函数:

  1. 当 m=n(即 N 为 偶数):需向A 添加一个元素。实现方法:将新元素 num 插入至 B ,再将 B 堆顶元素插入至 A ;
  2. 当 m != n(即 N 为 奇数):需向 B添加一个元素。实现方法:将新元素 num 插入至 A ,再将 A 堆顶元素插入至 B ;

findMedian() 函数:

当 m != n(即 N 为 奇数):则中位数为A 的堆顶元素。 当 m=n(即 N 为 偶数):则中位数为( A 的堆顶元素 + B
的堆顶元素 )/2。

复杂度分析

  • 时间复杂度: 查找中位数即获取堆顶元素O(1),添加数字操作即堆的插入和弹出操作O(logN)
  • 空间复杂度:小根堆和大根堆最多共同保存N个元素,即O(N)

代码实现:

import heapq

class MedianFinder:

    def __init__(self):
        """
        initialize your data structure here.
        """
        # python中的堆都是小根堆,即对顶元素一定是最小值
        # 大根堆的实现,用小根堆的出堆入堆操作再将数据取反实现
        # 小根堆->大根堆链接在一起:数据是从大到小的
        # 大根堆用于存储较小数据部分
        self.big_heap = []
        # 小根堆用于存储较大数据部分
        self.small_heap = []


    def addNum(self, num: int) -> None:
        # 如果两个堆的元素个数不同,则将数据存放到大根堆中
        # 存放的规则是先将数据push进小根堆,然后pop出堆顶元素到大根堆中
        if len(self.big_heap) != len(self.small_heap):
            heapq.heappush(self.small_heap, num)
            heapq.heappush(self.big_heap, -heapq.heappop(self.small_heap))
        else:
            # 两个堆元素的个数相同,则将数据存放到小根堆中
            # 存放的规则是先将数据push到大根堆中,然后pop出堆顶元素到小根堆中
            heapq.heappush(self.big_heap, -num)
            heapq.heappush(self.small_heap, -heapq.heappop(self.big_heap))

    def findMedian(self) -> float:
        # 计算中位数,区分奇偶情况
        return self.small_heap[0] if len(self.small_heap) != len(self.big_heap) else (self.small_heap[0] - self.big_heap[0]) / 2.0
        
# Your MedianFinder object will be instantiated and called as such:
# obj = MedianFinder()
# obj.addNum(num)
# param_2 = obj.findMedian()

参考文献:
https://leetcode-cn.com/problems/shu-ju-liu-zhong-de-zhong-wei-shu-lcof/solution/mian-shi-ti-41-shu-ju-liu-zhong-de-zhong-wei-shu-y/

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值