剑指 Offer 59 - I. 滑动窗口的最大值(单调队列)
- 题目描述
给定一个数组 nums 和滑动窗口的大小 k,请找出所有滑动窗口里的最大值。
示例:
输入: 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
提示:
你可以假设 k 总是有效的,在输入数组不为空的情况下,1 ≤ k ≤ 输入数组的大小。
- 分析
暴力求解:
直接从左往右遍历,计算每个滑动窗口的最大值。
class Solution: def maxSlidingWindow(self, nums, k): if not nums: return [] p1 = 0 res = [] while p1 <= len(nums)-k: res.append(max(nums[p1: p1+k])) p1 += 1 return res
当然这样肯定是拿不到offer的。。。
来我们看看用双端队列做的
首先我们需要了解下python自带的高级数据结构模块collections模块。
里面实现了类似双端队列的结构deque,它支持append(), appendleft(),extend(),extendleft(),pop(),popleft()等操作,可以实现左端append数据和pop数据,右端extend和pop数据。
让滑动窗口依次从左往右滑动,每次用一个单调队列(参考单调栈的做法)保存滑动窗口中的最大值的一个递减序列,这个单调队列的队首则是当前窗口的最大值。
如果这个最大值已经被滑过了,是窗口左端临近的第一个元素,并且是最大值,此时则将这个最大值从单调队列删除。看图可能理解更清晰一点:
class Solution: def maxSlidingWindow(self, nums, k): if not nums or k ==0: return [] deque = collections.deque() for i in range(k): #此时滑动窗口的初始位置,同样保持单调递减 while deque and deque[-1] < nums[i]: deque.pop() deque.append(nums[i]) res = [deque[0]] for i in range(k, len(nums)): if deque[0] == nums[i-k]: #将滑动窗口最左边旁边的当前为最大值的元素删除掉 deque.popleft() while deque and deque[-1] < nums[i]: #在滑动窗口里面始终保持单调递减,deque顶端元素始终是最大值 deque.pop() deque.append(nums[i]) res.append(deque[0]) return res
参考链接
https://docs.python.org/zh-cn/3/library/collections.html#deque-objects