题目表述:
给你一个整数数组 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]
提示:
1 <= nums.length <= 105
-104 <= nums[i] <= 104
1 <= k <= nums.length
力扣链接:
思路分析:
这道题是典型的单调队列的题目,如果用暴力方法,两个for循环来解题,复杂度是O(nx k)。此时我们需要一个队列,这个队列存储放进窗口的元素,随着窗口的移动,队列也一进一出,每次移动之后,队列告诉我们里面的最大值是什么。每次窗口移动时,调用que.pop, que.push, pue.front依次移除元素,添加元素,和获取窗口的最大值。
想到这里,大家肯定还有疑问,所谓单调队列,单调体现在哪里呢?下面我们就需要介绍了,其实队列没有必要维护窗口里的所有元素,只需要维护可能成为窗口里最大值的元素就可以了,同时保证队列里的元素数值是由大到小的。
具体怎么做呢?只需要在设计单调队列的时候,pop和push操作保持一下规则:1.pop:如果窗口移除的元素value等于单调队列的出口元素,那么队列弹出元素,否则不用任何操作;2.push:如果push的元素value大于入口元素的数值,那么就将队列入口的元素弹出,直到push元素的数值小于等于队列入口元素为止。保持以上规则,每次移动窗口的时候,只要调用que.front就可以返回当前窗口的最大值。
代码分析:
from collections import deque
class MyQueue: # 单调队列(从大到小)
def __init__(self):
self.queue = deque() # 这里需要使用deque实现单调队列,直接使用list会超时
# 每次弹出的时候,比较当前要弹出的数值是否等于队列出口元素的数值,如果相等则弹出。
# 同时pop之前判断队列当前是否为空
def pop(self, value):
if self.queue and value == self.queue[0]:
self.queue.popleft() # list.pop()时间复杂度为o(n), 这里需要使用collections.deque()
# 如果push的数值大于入口元素的数值,那么就将队列后端的数值弹出,直到push的数值小于等于队列入口元素的数值为止
# 这样就保持了队列里的数值是单调从大到小的了
def push(self, value):
while self.queue and value > self.queue[-1]:
self.queue.pop()
self.queue.append(value)
#查询当前队列里的最大值,直接返回队列前端也就是front就可以了
def front(self):
return self.queue[0]
class Solution:
def maxSlidingWindow(self, nums: List[int], k: int) -> List[int]:
que = MyQueue()
result = []
for i in range(k):
# 先将前k的元素放进队列
que.push(nums[i])
# result 记录前k的元素的最大值
result.append(que.front())
for i in range(k, len(nums)):
# 滑动窗口移除最前面元素
que.pop(nums[i-k])
# 滑动窗口前加入最后面的元素
que.push(nums[i])
# 记录对应的最大值
result.append(que.front())
return result