剑指 Offer 59

  • 题目描述

给定一个数组 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://leetcode-cn.com/problems/hua-dong-chuang-kou-de-zui-da-zhi-lcof/solution/mian-shi-ti-59-i-hua-dong-chuang-kou-de-zui-da-1-6/

https://docs.python.org/zh-cn/3/library/collections.html#deque-objects

原文地址:https://www.cnblogs.com/yeshengCqupt/p/13455117.html