【力扣-239. 滑动窗口最大值🚀】Python笔记

0 阅读3分钟

一、前置知识点:单调队列与滑动窗口

1. 滑动窗口(Sliding Window)

  • 定义:在数组 / 字符串上维护一个固定长度的 “窗口”,窗口随索引向右滑动,用于高效处理区间问题。
  • 核心痛点:若每次滑动后都遍历窗口求最大值,时间复杂度为 O (nk),数据量大时会超时。

2. 单调队列(Monotonic Queue)

  • 定义:一种特殊队列,内部元素始终保持单调递增 / 递减顺序。

  • 核心优势

    • 队首元素始终是当前窗口内的最大值(或最小值),O (1) 获取极值。
    • 入队时移除队尾所有比当前元素小的元素,保证队列单调性。
    • 队首元素超出窗口范围时,自动出队。
  • 实现:使用 Python collections.deque 实现双端队列,支持 O (1) 时间的两端操作。


二、经典算法题:滑动窗口最大值(LeetCode 239)

题目描述

给你一个整数数组 nums,有一个大小为 k 的滑动窗口从数组的最左侧移动到最右侧。你只能看到在滑动窗口内的 k 个数字。滑动窗口每次只向右移动一位。返回滑动窗口中的最大值。

示例

  • 输入nums = [1, 3, -1, -3, 5, 3, 6, 7]k = 3
  • 输出[3, 3, 5, 5, 6, 7]

最优解法:单调双端队列

核心思路

  1. 队列存储索引:队列中保存的是元素索引,而非值,方便判断是否超出窗口范围。
  2. 维护单调性:新元素入队前,移除队尾所有比它小的元素,保证队列从大到小排列。
  3. 移除过期元素:若队首索引超出当前窗口左边界,将其从队首弹出。
  4. 记录结果:当窗口形成(索引 i >= k-1)时,队首元素即为当前窗口最大值。

代码实现

from typing import List 
from collections import deque 
class Solution:
    def maxSlidingWindow(self, nums: List[int], k: int) -> List[int]: 
    q = deque() # 双端队列,存储索引,单调递减 
    res = [] 
    
    for i in range(len(nums)): 
        # 窗口左边界 
        left = i - k + 1 
        
        # 1. 维护单调递减:移除队尾所有 < 当前元素的索引 
        while q and nums[q[-1]] < nums[i]: 
            q.pop() 
            
        # 当前索引入队 
        q.append(i) 
        
        # 2. 移除超出窗口左边界的队首元素
        if q[0] < left:
            q.popleft() 
            
        # 3. 窗口形成后,记录当前窗口最大值(队首) 
        if i >= k - 1: 
            res.append(nums[q[0]]) 
    return res

代码执行示例

nums = [1, 3, -1, -3, 5, 3, 6, 7]k = 3 为例:

  1. i=0:队列 [0],窗口未形成
  2. i=1:移除 0,队列 [1],窗口未形成
  3. i=2:队列 [1, 2],窗口形成 → 记录 nums[1]=3
  4. i=3:队列 [1, 2, 3],窗口形成 → 记录 nums[1]=3
  5. i=4:移除 3, 2, 1,队列 [4],窗口形成 → 记录 nums[4]=5
  6. 后续依次得到 [5, 6, 7],最终结果 [3, 3, 5, 5, 6, 7]

三、关键知识点总结

1. 复杂度分析

  • 时间复杂度:O (n),每个元素最多入队和出队各一次。
  • 空间复杂度:O (k),队列最多存储 k 个元素。

2. 单调队列核心操作

操作作用
q.append(i)当前元素索引入队
q.pop()移除队尾,维护单调性
q.popleft()移除队首,清理超出窗口的元素
nums[q[0]]O (1) 获取当前窗口最大值

3. 适用场景拓展

单调队列 + 滑动窗口适用于:

  • 滑动窗口极值:本题、滑动窗口最小值
  • 单调栈 / 队列扩展:如柱状图中最大矩形、最大矩形面积等区间极值问题