线性复杂度求解滑动窗口最大值-单调队列实现
最近在leecode上碰到一个很有意思的题目,求解滑动窗口的最大值(https://leetcode-cn.com/problems/sliding-window-maximum/), 题目截图如下:

其实要写出程序解决这个问题很简单啦,但是为啥会被力扣认为是hard难度的题目呢?秘密在于图片的最下方。看到了嘛,数据量达到了10^5次方,这基本说明了一件事情——只有线性复杂度的算法代码能提交通过!
本人实际也想不到线性复杂度的算法来做这个事,然后参考了官方的题解之后明白了,关键在于单调队列。
单调队列,顾名思义是保持单调性的队列。试想若每个窗口的元素我们都有这样一个对应的单调递减队列,那窗口的最大值就是单调队列的第一个元素。
单调队列的实现(以单调递减队列为例),实际与单调栈类似。要添加新元素进入队列时,将其与队尾元素比较,若队尾元素比新元素小,则将队尾元素弹出,直到队尾元素不小于新元素,再将新元素添加进来,成为新的队尾元素。
我的代码如下:
from collections import deque class Solution: def maxSlidingWindow(self, nums, k): '''线性复杂度实现滑动窗口最大值-单调递减队列法''' # 因为在队列两端都有删除操作,这里我们采用双向队列实现单调队列 if nums == []: return [] else: deq = deque() maxlist = [] #初始操作前k个, 注意加入队列的是索引 for i in range(k): while deq and nums[i] > nums[deq[-1]]: #保证单调性 deq.pop() deq.append(i) maxlist.append(nums[deq[0]]) #单调队列第一个元素即为当前窗口最大值的索引 #窗口开始滑动,注意此时需要删除上一窗口最左边的元素了 for i in range(k, len(nums)): #注意deq[0]肯定是deq中关于nums列表索引最小的,我们只需要判断其是否是上一窗口最左元素(若不是说明上一窗口最左元素已经被删除) if deq and deq[0] == i-k: deq.popleft() while deq and nums[i] > nums[deq[-1]]: #保证单调性 deq.pop() deq.append(i) maxlist.append(nums[deq[0]]) return maxlist #线性复杂度,因为列表中每个元素至多补被操作两次,添加一次,弹出一次
因为要队列的队尾既有插入操作也有删除操作,因此我们采用双向队列来实现单调队列,这里直接使用的 Python 中的 collections.deque 方法。
算法的其他细节代码中有详细注释,就不再过多解释啦!

浙公网安备 33010602011771号