day8-2022.11.02

作者:Krahets

滑动窗口的最大值

给定一个数组 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

题解一:个人尝试过程

是模仿栈的最小值那道题,但是报错了,报错的用例是超长的那道。并且我的方法在这个用例弊端也暴露出来了。参考官方解答的方法是保留降序的非严格排序的队列。那我的方法可能是超时了吧。

class Solution:
    def maxSlidingWindow(self, nums: List[int], k: int) -> List[int]:
        if not nums:return
        max_num = None                  # 存储最大值
        max_idx = None                  # 存储最大值的idx
        max_list = []                   # 返回值
        # 开始滑动,这里的i定位的是窗口的尾端值,包含了第一轮
        for i in range(k-1, len(nums)):
            # 先检查是否需要弹出最大值,滑动窗体第一步,删除窗首端元素,当不是第一个窗体且最大值不为空
            if i!=k-1 and max_num:
                # 如果窗体的滑出的那个元素等于最大元素,则删除,注:窗体首个元素为i-k+1
                if (i-k)==max_idx:
                    max_idx = None
                    max_num = None
            # 如果最大值为空,则需要遍历整个窗体
            if not max_num:
                for j in range(i-k+1, i+1):
                    # 如果max_num为空,则本轮循环只是赋值,一般是第一轮遍历
                    if not max_num:
                        max_num = nums[j]
                        max_idx = j
                    # 如果max_num不为空,
                    elif nums[j]>=max_num:
                        max_num = nums[j] 
                        max_idx = j
            else:
                # 检查队尾是否需要更新最大值
                if nums[i]>=max_num:
                    max_num = nums[i]
                    max_idx = i
            max_list.append(max_num)
        
        return max_list
        # 暴力方法遍历会超时,先想办法试试那个栈的最大值的办法,有点类似,如果窗体出了那个值,就pop掉

修改上面的代码为一个队列存储,但是这个还是存在问题。问题出现在 如果max_num不为空,如果nums[j]大于最大值,max_num重新开一车 。这种情况下,当最大值pop出去之后,次大值因为小于最大值而未被记录,导致返回了一个小的值。

class Solution:
    def maxSlidingWindow(self, nums: List[int], k: int) -> List[int]:
        if not nums:return
        max_num = []                    # 存储最大值
        max_list = []                   # 返回值
        # 开始滑动,这里的i定位的是窗口的尾端值,包含了第一轮
        for i in range(k-1, len(nums)):
            # 先检查是否需要弹出最大值,滑动窗体第一步,删除窗首端元素,当不是第一个窗体且最大值列表不为空
            if i!=k-1 and max_num:
                # 如果窗体的滑出的那个元素等于最大元素,则删除,注:窗体首个元素为i-k+1
                if nums[i-k]==max_num[0]:
                    max_num.pop(0)
            # 如果最大值列表为空,则需要遍历整个窗体
            if not max_num:
                for j in range(i-k+1, i+1):
                    # 如果max_num为空,则本轮循环只是赋值,一般是第一轮遍历
                    if not max_num:
                        max_num.append(nums[j])
                    # 如果max_num不为空,如果nums[j]大于最大值,max_num重新开一车
                    elif nums[j]>=max_num[0]:
                        max_num = [nums[j]]
                    # 如果nums[j]小于末端值,
                    elif nums[j]<max_num[-1]:
                        max_num.append(nums[j])
                        
            else:
                # 检查队尾是否需要更新最大值
                if nums[i]>=max_num[0]:
                    max_num = [nums[i]]
                elif nums[i]<max_num[-1]:
                    max_num.append(nums[i])
            max_list.append(max_num[0])
        
        return max_list
        # 暴力方法遍历会超时,先想办法试试那个栈的最大值的办法,有点类似,如果窗体出了那个值,就pop掉

在上面错误的基础上继续更新,成功通过了,但是很花时间,在超时的边缘了属于是,再次尝试修改一下吧

class Solution:
    def maxSlidingWindow(self, nums: List[int], k: int) -> List[int]:
        if not nums:return
        max_num = []                    # 存储最大值
        max_list = []                   # 返回值
        # 开始滑动,这里的i定位的是窗口的尾端值,包含了第一轮
        for i in range(k-1, len(nums)):
            # 先检查是否需要弹出最大值,滑动窗体第一步,删除窗首端元素,当不是第一个窗体且最大值列表不为空
            if i!=k-1 and max_num:
                # 如果窗体的滑出的那个元素等于最大元素,则删除,注:窗体首个元素为i-k+1
                if nums[i-k]==max_num[0]:
                    max_num.pop(0)
            # 如果最大值列表为空,则需要遍历整个窗体
            if not max_num:
                for j in range(i-k+1, i+1):
                    # 如果max_num为空,则本轮循环只是赋值,一般是第一轮遍历
                    if not max_num:
                        max_num.append(nums[j])
                    # 如果max_num不为空,如果nums[j]大于最大值,max_num重新开一车
                    else:
                        while max_num and max_num[-1]<nums[j]:
                            max_num.pop()
                        max_num.append(nums[j])
                        
            else:
                # 检查队尾是否需要更新最大值
                while max_num and max_num[-1]<nums[i]:
                    max_num.pop()
                max_num.append(nums[i])
            max_list.append(max_num[0])
        
        return max_list
        # 暴力方法遍历会超时,先想办法试试那个栈的最大值的办法,有点类似,如果窗体出了那个值,就pop掉

简化了一下代码:

for j in range(i-k+1, i+1):
                    # 如果max_num为空,则本轮循环只是赋值,一般是第一轮遍历
                    if not max_num:
                        max_num.append(nums[j])
                    # 如果max_num不为空,如果nums[j]大于最大值,max_num重新开一车
                    else:
                        while max_num and max_num[-1]<nums[j]:
                            max_num.pop()
                        max_num.append(nums[j])

上面这部分可以简化,if not max_num 的判断有点多余,因为如果 max_num 为空,就直接在 else 部分append 了。

class Solution:
    def maxSlidingWindow(self, nums: List[int], k: int) -> List[int]:
        if not nums:return
        max_num = []                    # 存储最大值
        max_list = []                   # 返回值
        # 前k个,是必须遍历的,这时候不需要返回,max_list不需要增加
        for i in range(k):
            # 如果i为0,先赋初值
            while max_num and max_num[-1]<nums[i]:
                max_num.pop()
            max_num.append(nums[i])
        max_list.append(max_num[0])
        # 开始滑动
        for i in range(k, len(nums)):
            # 弹出
            if nums[i-k]==max_num[0]:
                max_num.pop(0)
            # 更新值
            while max_num and max_num[-1]<nums[i]:
                max_num.pop()
            max_num.append(nums[i])
            max_list.append(max_num[0])        
        
        return max_list
        # 暴力方法遍历会超时,先想办法试试那个栈的最大值的办法,有点类似,如果窗体出了那个值,就pop掉

好像我的暂时的处理就到这里了,看一下参考代码再改善吧。哇,不好意思,哈哈哈,官方给的方法就是这样的。但是依然有超时的可能,应该是python本身效率不高的问题。