📌  相关文章
📜  滑动窗口最大值(所有大小为 k 的子数组的最大值)(1)

📅  最后修改于: 2023-12-03 15:11:08.889000             🧑  作者: Mango

滑动窗口最大值

滑动窗口最大值问题,即在一个长度为n的数组中,大小为k的滑动窗口从左向右移动,每次窗口滑动一次,找出当前窗口中的最大值,输出所有窗口中的最大值。本文将介绍解决滑动窗口最大值问题的多种算法。

解法一:暴力法

暴力法的思路是对于每个窗口都遍历一次,找出其中的最大值。时间复杂度是$O(n*k)$,空间复杂度为$O(1)$。

def max_in_window(nums: List[int], k: int) -> List[int]:
    if not nums or len(nums) < k:
        return []
    res = []
    for i in range(len(nums) - k + 1):
        res.append(max(nums[i:i+k]))
    return res
解法二:堆

堆方法的思路是维护一个大小为k的最大堆,每次更新堆时,插入新的数之前,需要将窗口之外的数从堆中删除。时间复杂度为$O(nlogk)$,空间复杂度为$O(k)$。

import heapq
def max_in_window(nums: List[int], k: int) -> List[int]:
    if not nums or len(nums) < k:
        return []
    res = []
    heap = [-num for num in nums[:k]]
    heapq.heapify(heap)
    res.append(-heap[0])
    for i in range(k, len(nums)):
        heapq.heappush(heap, -nums[i])
        heapq.heappop(heap)
        res.append(-heap[0])
    return res
解法三:双端队列

双端队列方法的思路是维护一个双端队列,队列里面存放的是数组下标。每次遍历到一个新的数时,需要将队列中小于当前数的数的下标从队列的右端移除。同时,也要判断队列的左端是否已经窗口之外,如果是,则从队列左端移除。时间复杂度为$O(n)$,空间复杂度为$O(k)$。

def max_in_window(nums: List[int], k: int) -> List[int]:
    if not nums or len(nums) < k:
        return []
    res = []
    q = collections.deque()
    for i in range(len(nums)):
        # 如果队列中存放的数已经不在窗口中,则将其删除
        if q and q[0] < i - k + 1:
            q.popleft()
        # 如果队列中存放的数比当前数小,则将其删除
        while q and nums[q[-1]] < nums[i]:
            q.pop()
        q.append(i)
        # 当达到窗口大小时,记录最大值
        if i >= k - 1:
            res.append(nums[q[0]])
    return res
总结

以上就是三种解决滑动窗口最大值问题的方法。其中,暴力法最简单但效率最低,堆和双端队列的时间和空间复杂度都要好于暴力法。在实际应用中,需要根据数据量的大小和算法的实现细节来选择合适的算法。