LeetCode题目(Python实现):滑动窗口最大值

题目

给定一个数组 nums,有一个大小为 k 的滑动窗口从数组的最左侧移动到数组的最右侧。你只可以看到在滑动窗口内的 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

提示

  • 1 <= nums.length <= 10^5
  • -10^4 <= nums[i] <= 10^4
  • 1 <= k <= nums.length

想法一:利用辅助栈

先找一个最简单、最容易实现的想法。

将滑动窗口内的元素加入到辅助栈中,然后移动滑动窗口的同时更新辅助栈,使其元素和滑动窗口内的元素相同,然后取最大值加入到答案列表中。

算法实现

def maxSlidingWindow(self, nums: List[int], k: int) -> List[int]:
    slid = nums[:k]
    res = [max(slid)]
    i = k
    n = len(nums)
    while i < n:
        slid.pop(0)
        slid.append(nums[i])
        res.append(max(slid))
        i += 1
    return res

执行结果

执行结果 : 通过
执行用时 : 460 ms, 在所有 Python3 提交中击败了30.01%的用户
内存消耗 : 17.4 MB, 在所有 Python3 提交中击败了100.00%的用户
在这里插入图片描述

复杂度分析

  • 时间复杂度:O(Nk)。其中 N 为数组中元素个数。
  • 空间复杂度:O(N)

双端队列

用双端队列表示滑动窗口,同时保持左端为最大元素索引。

算法实现

def maxSlidingWindow(self, nums: List[int], k: int) -> List[int]:
    n = len(nums)
    if n * k == 0:
        return []
    if k == 1:
        return nums

    def clean_deque(i):
        # remove indexes of elements not from sliding window
        if deq and deq[0] == i - k:
            deq.popleft()

        # remove from deq indexes of all elements
        # which are smaller than current element nums[i]
        while deq and nums[i] > nums[deq[-1]]:
            deq.pop()

    # init deque and output
    deq = deque()
    max_idx = 0
    for i in range(k):
        clean_deque(i)
        deq.append(i)
        # compute max in nums[:k]
        if nums[i] > nums[max_idx]:
            max_idx = i
    output = [nums[max_idx]]

    # build output
    for i in range(k, n):
        clean_deque(i)
        deq.append(i)
        output.append(nums[deq[0]])
    return output

执行结果

在这里插入图片描述

复杂度分析

  • 时间复杂度:O(N),每个元素被处理两次- 其索引被添加到双向队列中和被双向队列删除。
  • 空间复杂度:O(N),输出数组使用了 O(N−k+1) 空间,双向队列使用了 O(k)。

动态规划

算法实现

def maxSlidingWindow(self, nums: List[int], k: int) -> List[int]:
    n = len(nums)
    if n * k == 0:
        return []
    if k == 1:
        return nums

    left = [0] * n
    left[0] = nums[0]
    right = [0] * n
    right[n - 1] = nums[n - 1]
    for i in range(1, n):
        # from left to right
        if i % k == 0:
            # block start
            left[i] = nums[i]
        else:
            left[i] = max(left[i - 1], nums[i])
        # from right to left
        j = n - i - 1
        if (j + 1) % k == 0:
            # block end
            right[j] = nums[j]
        else:
            right[j] = max(right[j + 1], nums[j])

    output = []
    for i in range(n - k + 1):
        output.append(max(left[i + k - 1], right[i]))

    return output

执行结果

在这里插入图片描述

复杂度分析

  • 时间复杂度:O(N),我们对长度为 N 的数组处理了 3次。
  • 空间复杂度:O(N),用于存储长度为 N 的 left 和 right 数组,以及长度为 N - k + 1的输出数组。

双端队列简化版

算法实现

def maxSlidingWindow(self, nums: List[int], k: int) -> List[int]:
    deq = deque()
    res = []
    for i in range(len(nums)):
        # 移除队头
        if deq and deq[0] <= i - k:
            deq.popleft()
        # 移除小于当前要加入元素的值的索引
        while deq and nums[i] > nums[deq[-1]]:
            deq.pop()
        # 加入当前索引
        deq.append(i)
        # 更新最大值
        if i >= k - 1:
            res.append(nums[deq[0]])
    return res

执行结果

在这里插入图片描述

小结

题目难度应该为中等,暴力法虽然简单,但是无法达到线性要求,双端队列和动态规划是两种比较难的方法,思路比较重要,这道题没有见过,所以思路比较少,不知道该用什么,以后遇到类似问题思路就会好很多了。

  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
根据引用\[1\],可以使用暴力解法来求解滑动窗口的最大。具体的做法是,遍历数组,对于每个窗口,使用一个内部循环来找到窗口中的最大,并将其存储在结果数组中。时间复杂度为O(n*k),其中n为数组长度,k为窗口大小。 根据引用\[2\],还可以使用队列来求解滑动窗口的最大。具体的做法是,使用一个双端队列来维护一个单调递减的窗口。遍历数组,对于每个元素,首先判断队头是否在滑动窗口范围内,如果不在,则将其从队头移除。然后,将当前元素与队尾元素比较,如果当前元素大于队尾元素,则将队尾元素移除,直到队列为空或者当前元素小于等于队尾元素。最后,将当前元素的索引插入队尾。如果滑动窗口的元素个数达到了k个,并且始终维持在窗口中,就将队头元素加入答案数组中。时间复杂度为O(n),其中n为数组长度。 综上所述,可以使用暴力解法或者使用队列来求解leetcode滑动窗口的最大。 #### 引用[.reference_title] - *1* *3* [leetcode239. 滑动窗口最大](https://blog.csdn.net/kkkkuuga/article/details/124829581)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^koosearch_v1,239^v3^insert_chatgpt"}} ] [.reference_item] - *2* [Leetcode#239. 滑动窗口最大 (Java解法)](https://blog.csdn.net/paranior/article/details/114890555)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^koosearch_v1,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值