239. 滑动窗口最大值

image.png

解法一:单调队列+二分查找

思路

单调队列里的元素都是递增的。

每次滑动窗口,将当前元素通过二分法插入到队列中,同时从队列中删除窗口第一个元素,此时也是使用二分查找。

复杂度:o(logk*n)

关于单调队列的题目,我记得187周赛好像也有一道题是的,第三题,有兴趣可以去看我的那篇博客。

187周赛总结

代码
class Solution:
    # 方法 1:单调队列+二分查找
    # 复杂度: o(nlogk)
    def maxSlidingWindow(self, nums: List[int], k: int) -> List[int]:
        ans, queue = [], []
        for index, x in enumerate(nums):
            bisect.insort_right(queue, x)
            ans.append(queue[-1])
            if len(queue) >= k:
                del queue[bisect.bisect_left(queue, nums[index - k + 1])]
        return ans[k - 1:] if k <= len(nums) else [max(nums)]

解法二:双端队列

思路:

维护一个队列,这个队列中保存的是到目前为止,窗口中出现的值的索引(部分)。队列中的索引对应的值从大到小排,索引从小到大,也就是和原数组的相对顺序一致。为什么是部分?看下面就知道了。

当前元素添加到队列的时候,要把小于他的元素全部删除,这些元素不仅更“老”而且还“小”,所以没有什么用了。

还需要注意的是窗口滑动的时候要删除不在窗口的位置,这个时候就判断队列的第一个元素(最老的)是不是在窗口外,如果是的话就把她pop出去,这个时候最大值就变成了什么??是不是之后加入的最大值!!
简直amazing!! 巧妙!!

代码如下。

class Solution:
    # 方法 2:双端队列
    def maxSlidingWindow(self, nums: List[int], k: int) -> List[int]:
        ans, queue = [], deque()
        for index in range(0, len(nums)):
            # 添加元素的时候先删除第一个
            if queue and queue[0] == index - k:
                queue.popleft()
            # 小于当前添加元素的数出队列
            while queue and nums[queue[-1]] < nums[index]:
                queue.pop()
            # 当前元素入队列
            queue.append(index)
            if index>=k-1 or index==len(nums)-1:
                ans.append(nums[queue[0]])
        return ans

北语张益达
6 声望4 粉丝