[leetcode] 480. 滑动窗口中位数 python 对顶堆 同时使用大根堆、小根堆求中位数

中位数是有序序列最中间的那个数。如果序列的长度是偶数,则没有最中间的数;此时中位数是最中间的两个数的平均数。

例如:

[2,3,4],中位数是 3
[2,3],中位数是 (2 + 3) / 2 = 2.5
给你一个数组 nums,有一个长度为 k 的窗口从最左端滑动到最右端。窗口中有 k 个数,每次窗口向右移动 1 位。你的任务是找出每次窗口移动后得到的新窗口中元素的中位数,并输出由它们组成的数组。

解法:使用一个大根堆、一个小根堆,将小于中位数的放到大根堆中(使大根堆中全是较小的数),大于中位数的放到小根堆中,并维持两个堆的平衡(k为偶数时两堆相等,为奇数时大根堆比小根堆多一个),这样中位数必然会出自两个堆的堆顶(k为偶数时为两堆顶的平均数,为奇数时为大根堆的堆顶)。
在这里插入图片描述
tips: python中模块只能实现小根堆,为了实现大根堆采取了加负号的方法,在使用过程中要注意负号的处理。

class Solution:
    def medianSlidingWindow(self, nums: List[int], k: int) -> List[float]:

        if k == 1: return nums

        import heapq
        # 初始化:建立一个大根堆small、一个小根堆big,(大根堆中放的全是比中位数小的数,小根堆中放的全是比中位数大的数)
        # 把前k个数放到small中,然后从small中弹出k//2个放到big中,
        # 当k为奇数时,small比big多一个,为偶数时相等。
        small, big, ans = [], [], []
        for i in nums[:k]:
            heapq.heappush(small, -i)
        for i in range(k // 2):
            heapq.heappush(big, -heapq.heappop(small))
        
        # 求中位数
        def get():
            if k % 2 == 0:
                return (-small[0] + big[0]) / 2
            else:
                return - small[0]
        # 对两个堆进行调整,使两个堆大小保持平衡(k为偶数时相等,为奇数时small比big多一个)
        def adj():
            if len(small) - len(big) > 1:  #解题过程中small最多只会比big多两个,所以只处理一次就行了
                heapq.heappush(big, -heapq.heappop(small))
            while len(big) - len(small) >= 1:  #small要等于或比big多一个,所以可能要处理两次
                heapq.heappush(small, -heapq.heappop(big))

        ans.append(get())  #求第一个中位数

        n = len(nums)
        for left in range(1, n-k+1):

            # 添加进入窗口的元素:如果比大根堆的堆顶还小,说明属于小的部分(大根堆)
            if nums[left + k - 1] <= -small[0]:
                heapq.heappush(small, -nums[left + k - 1])
            else:
                heapq.heappush(big, nums[left + k - 1])

            adj()

            # 将窗口左边的从堆中移出,如果比小根堆的堆顶大,说明属于大的部分。
            if nums[left - 1] >= big[0]:
                big.remove(nums[left - 1])
                heapq.heapify(big)
            else:
                small.remove(-nums[left - 1])
                heapq.heapify(small)
            
            adj()

            # 求中位数
            ans.append(get())

        return ans

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值