239.滑动窗口最大值|堆|优先队列|单调递减的双向队列

在这里插入图片描述

一、最小堆

  • 堆真是个好数据结构,可惜不熟。
class Solution:
    def maxSlidingWindow(self, nums: List[int], k: int) -> List[int]:
        # 如果窗口大小为1,滑动窗口最大值就是自身
        if k == 1: return nums
        
        # 一、错误思路
        # firstMax = [nums[0], 0] # 记录窗口中第一大的数值和其下标
        # secondmax = [nums[1], 1] # 记录窗口中第二大的数值和其下标
        # 可惜对堆不太熟练,想不到用它T^T
        nlen = len(nums)
        # dp = [0] * (nlen - k + 1)

        # # 初始状态
        # for i in range(k):

        # 二、小根堆/优先队列

        # python默认小根堆,即最小的在前面;目的是求最大值,取负值变成最小值,这样就可排在最前面。
        pQueue = [(-nums[i], i) for i in range(k)] # 记录数值和下标
        # 将列表转换成小根堆
        heapq.heapify(pQueue) 

        res = [-pQueue[0][0]] # 第一个窗口的最大值,小根堆最小值取负就是了

        for i in range(k, nlen):
            heapq.heappush(pQueue, (-nums[i], i)) # 将新来的数值和下标入小根堆
            while pQueue[0][1] <= i - k: 
            # 如果堆中最小值的下标已经超出k窗口, while直到不越界;
            # 因为这个代码不是进一个删一个的逻辑,是一个一个进,等最小值越界才一个一个删除。
                heapq.heappop(pQueue) # 删除这个最小值
            res.append(-pQueue[0][0]) # 取当前堆中最小值,取负成最大值

        return res

        

二、单调递减的双向队列

  • 用递减的队列[其中记录的是下标,以判断是否越界]维持窗口中的最大值max1、第二个最大值max2、第三个最大值…
  • 假设在最大值max1下标前有个数值tmp大于第二个最大值max2,但因为窗口往右移动,最大值max1的光芒会掩盖掉tmp,tmp在窗口中,最大值window_max不会是它,tmp不在窗口中,最大值window_max更不会是它。tmp会比max1先消亡,保存tmp没有意义。保存max2的意义就是怕max1的下标在移动窗口后越界了,这时max2顶上去当老大。
class Solution:
    def maxSlidingWindow(self, nums: List[int], k: int) -> List[int]:
        # 如果窗口大小为1,滑动窗口最大值就是自身
        if k == 1: return nums
        
        nlen = len(nums)

        # 三、单调的双向队列
        # 用递减的队列[其中记录的是下标,以判断是否越界]维持窗口中的最大值、第二个最大值、第三个最大值...,我的思路也类似,但是没想那远,只想到用单调栈。
        queue = collections.deque() # 使用包中的双向队列
        for i in range(k):
            while queue and nums[i] >= nums[queue[-1]]: # 与队列最小的值比较
                queue.pop() # 在右边删除小于nums[i]的数,一直while循环删除
            queue.append(i) # 添加这个下标;维持递减的队列
        # 此时队列中的第一个是这窗口中的最大值的下标

        res = [nums[queue[0]]]
        for i in range(k, nlen):
            while queue and nums[i] >= nums[queue[-1]]:
                queue.pop()
            queue.append(i) # 维持递减队列
            while queue[0] <= i - k:
                queue.popleft() # 这里就是要用双向队列的原因,要在左边删除越界了的最大值
            res.append(nums[queue[0]])

        return res

        

参考官方解析的代码

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
单调队列是一种特殊的数据结构,用于解决滑动窗口问题。在滑动窗口问题中,要求在一个大小为k的窗口中,找到窗口内的最大值或最小值。单调队列通过维护一个单调递增或递减队列,可以高效地解决这个问题。 具体实现上,可以使用两个单调队列,一个用于维护窗口内的最大值,另一个用于维护窗口内的最小值。在添加新元素时,如果队列不为空且要加入的元素值小于队列尾的值,则将队尾弹出,直到队尾小于要加入的元素或者队列为空。这样,队首就是当前窗口内的最大值或最小值了。 对于问题中提到的代码,首先定义了一个用于存储下标的队列stk,以及用于存储数组元素的数组a。然后通过一个循环,分别计算了窗口内的最小值和最大值。在计算最小值时,维护了一个单调递增的队列,即队首是窗口内的最小值。在计算最大值时,维护了一个单调递减队列,即队首是窗口内的最大值。最后,输出计算结果。 总结起来,单调队列是一种解决滑动窗口问题的高效数据结构,可以通过维护单调递增或递减队列来求解窗口内的最大值或最小值。<span class="em">1</span><span class="em">2</span><span class="em">3</span><span class="em">4</span> #### 引用[.reference_title] - *1* [整数滑动窗口(acwing算法题C++)](https://blog.csdn.net/falldeep/article/details/118631830)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatgptT3_1"}}] [.reference_item style="max-width: 33.333333333333336%"] - *2* [【Week5作业 D】滑动窗口单调队列】](https://download.csdn.net/download/weixin_38647039/14916076)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatgptT3_1"}}] [.reference_item style="max-width: 33.333333333333336%"] - *3* *4* [【acwing】单调队列--154. 滑动窗口](https://blog.csdn.net/qq_43531919/article/details/115402227)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatgptT3_1"}}] [.reference_item style="max-width: 33.333333333333336%"] [ .reference_list ]

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值