数据流的中位数 python 双堆

思路
要取得中位数,所以只要关注前一半数字的最小值和后一段数字的最大值,采用堆来进行维护
https://leetcode-cn.com/problems/shu-ju-liu-zhong-de-zhong-wei-shu-lcof/solution/jian-zhi-41shu-ju-liu-zhong-wei-shu-shua-w7yh/
# 奇数时向小根堆添加,但是确保向小根堆中添加元素比大根堆元素大,故先加入大根堆,将大根堆堆顶弹出给小根堆
#偶数时向左边大根堆添加,但是确保向大根堆添加元素均比小根堆元素小,故先加入小根堆,将小根堆堆顶弹出给大根堆
#注意,python中只有小根堆,故大根堆采用小根堆+取反操作实现
# 插入负数到小根堆中,越大的数字插入的负数就越小,所以这样就相当于做了大根堆,注意弹出时再取反得到正数即可

class MedianFinder:

    def __init__(self):
        """
        initialize your data structure here.
        """
        self.A = []#小根堆
        self.B = []#大根堆
##注意新生先入普通班(大顶堆),此时可能会失去平衡了!
    def addNum(self, num):
        if len(self.A) != len(self.B):
            heapq.heappush(self.A , num)
            heapq.heappush(self.B , -heapq.heappop(self.A))
        else:
            heapq.heappush(self.B , -num)
            heapq.heappush(self.A , -heapq.heappop(self.B))

    def findMedian(self):
        return self.A[0] if len(self.A) != len(self.B) else (self.A[0] - self.B[0])/2.0

用大顶堆+小顶堆方法,可以看作大顶堆是普通班,小顶堆是实验班。数量上时刻保持 小顶-大顶<=1(两堆相等或者小顶比大顶多一个)。

新学生先入普通班(大顶堆),此时可能会失去平衡了,于是取大顶堆的第一个(班里最好的学生)加入实验班(小顶堆),判断若数量过多(不是等于或多一个),取第一个(实验班里最差的学生)到普通班(大顶堆)里。 取中位数的时候,若两堆数量相等,则各取堆顶取平均,若小顶比大顶多一,则多的那一个就是中位数。

利用堆排序,构造两个堆,一个小顶堆big用来存储数据流中比较大的数,一个大顶堆small用来存储数据流中比较小的数,另外必须保证两个堆的大小相差不能超过1,这样一来,如果数据流总数是偶数,那么结果就是两个堆的堆顶元素之和除以2,如果是奇数,哪个堆的数据元素多,就返回哪个堆的堆顶。

1.为什么这个思路是对的?

从这个角度来看,如果把数据从小到大排列,把数据分成两组,左边的数比右边的数都小,如果数据总量是奇数,那么一定返回的是中间的数,这个中间的数对于较小的数据(大顶堆)来说是最大的数(堆顶),对于较大的数据来说(小顶堆)是最小的数(堆顶),所以哪个堆的元素多,就返回哪个堆的堆顶。

如果数据流是偶数,那么结果就是中间的两个数的和再除以2,那么也就是说一个数是较小数据的最大值,一个数是较大数据的最小值,结果就是两个堆的堆顶元素之和除以2。

进一步地,也就是说,不需要对数进行精确排序,我只要能保证一个堆里都是较小的数,一个堆里都是较大的数,也就是大致分个组,并且保证两个堆的尺寸相差不超过1即可。

2.问题来了,如何保证把数据能够大致分组?

这样做,如果small堆(放较小的数)为空,优先把数放到这里。接着,来了一个数据,如果这个数据比small.top()小,那我就把插入small里面,如果比它大,就放到big里面,

如果small.size()-big.size()==2,就把small的堆顶元素弹出放到big里面,因为small的堆顶元素是较小数据里面最大的那个,放到big里面,可保证big里面的数都是偏大的。

如果big.size()-small.size()==2,就把big的堆顶元素弹出放到small里面,因为big的堆顶元素是较大数据里面最小的那个,放到small里面,可保证small里面的数都是偏小的。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值