239 滑动窗口最大值(单调递减队列)

1. 问题描述: 

给你一个整数数组 nums,有一个大小为 k 的滑动窗口从数组的最左侧移动到数组的最右侧。你只可以看到在滑动窗口内的k个数字。滑动窗口每次只向右移动一位。返回滑动窗口中的最大值。

示例 1:

输入:nums = [1,3,-1,-3,5,3,6,7], k = 3
输出:[3,3,5,5,6,7]
解释:
滑动窗口的位置              最大值
---------------                      -----
[1  3  -1] -3  5  3  6  7       3
 1 [3  -1  -3] 5  3  6  7       3
 1  3 [-1  -3  5] 3  6  7       5
 1  3  -1 [-3  5  3] 6  7       5
 1  3  -1  -3 [5  3  6] 7       6
 1  3  -1  -3  5 [3  6  7]      7

示例 2:

输入:nums = [1], k = 1
输出:[1]

示例 3:

输入:nums = [1,-1], k = 1
输出:[1,-1]

示例 4:

输入:nums = [9,11], k = 2
输出:[11]

示例 5:

输入:nums = [4,-2], k = 2
输出:[4]

提示:

1 <= nums.length <= 10 ^ 5
-10 ^ 4 <= nums[i] <= 10 ^ 4
1 <= k <= nums.length

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/sliding-window-maximum

2. 思路分析:

分析题目可以知道我们需要维护一个长度为k的窗口的最大值,维护长度为k的窗口的最值可以使用单调队列来解决,在从前往后遍历nums的过程中维护长度小于等于k的单调递减的队列,因为需要在队列的两端进行操作所以需要声明一个双端队列(队列中一端需要插入与删除,另一端需要删除),python语言可以使用collections.deque()声明一个双端队列,当遍历到当前元素nums[i]的判断当前窗口长度是否大于k如果大于k说明我们需要删除掉窗口最左边的值,判断当前遍历的元素nums[i]是否大于等于队尾元素,如果大于等于队尾元素说明队尾元素已经没用存在的必要了,直接删除掉队尾元素即可,循环结束之后将当前元素的下标i加入到队尾即可(队列中加入的是元素下标这样我们可以通过队列中的元素就可以知道元素的下标以及对应的元素值,通过下标就可以知道窗口的长度),这样队列中对应下标的元素就是单调递减的,队头元素对应的元素值是最大的,当当前元素下标大于等于k - 1的时候将队头元素对应的元素值加入到结果集中即可。

3. 代码如下:

使用双端队列的写法:

from typing import List
import collections


class Solution:
    # 维护一个单调递减的队列
    def maxSlidingWindow(self, nums: List[int], k: int) -> List[int]:
        queue = collections.deque()
        res = list()
        for i in range(len(nums)):
            # 当队列长度超过k之后
            if queue and i - queue[0] >= k: queue.popleft()
            # 维护单调队列
            while queue and nums[queue[-1]] <= nums[i]:
                queue.pop()
            queue.append(i)
            if i >= k - 1: res.append(nums[queue[0]])
        return res

数组模拟双端队列的写法:hh,tt分别表示队列头指针和尾指针,当tt = -1时表示队列中没有元素,当tt = 0时表示存在一个元素:

from typing import List

class Solution:
    def maxSlidingWindow(self, nums: List[int], k: int) -> List[int]:
          n = len(nums)
          # 在nums前面添加一个0这样下标从1开始比较好处理
          nums = [0] + nums
          q = [0] * (n + 1)
          res = list()
          # tt = -1时表示没有元素, tt = 0时表示存在一个元素
          hh, tt = 0, -1
          for i in range(1, n + 1):
               # 当前位置划出了窗口
               if hh <= tt and i - k >= q[hh]: hh += 1
               # 队尾元素已经没有作用了
               while hh <= tt and nums[i] >= nums[q[tt]]: tt -= 1
               tt += 1
               q[tt] = i
               # 只有当窗口长度大于等于k之后说明才是符合条件的
               if i >= k: res.append(nums[q[hh]])
          return res
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值