当前位置: 首页 > news >正文

力扣(LeetCode)100题:239.滑动窗口最大值

239.滑动窗口最大值

我的题解:暴力--超时┭┮﹏┭┮

class Solution:def maxSlidingWindow(self, nums: List[int], k: int) -> List[int]:key=[]for j in range(len(nums)-k+1):k_max=nums[j]for i in range(j,j+k):if nums[i]>k_max:k_max=nums[i]key.append(k_max)return key

官方题解:

一、优先队列

这段代码使用优先队列(最大堆) 来解决「滑动窗口最大值」问题(LeetCode 239)。


class Solution:def maxSlidingWindow(self, nums: List[int], k: int) -> List[int]:n = len(nums)# Python 的 heapq 是小根堆(最小堆),但我们想要最大值。# 因此我们将数值取负(-nums[i]),这样最小堆就能模拟“最大堆”的行为。# 同时存储 (负值, 索引),以便后续判断元素是否还在当前窗口内。q = [(-nums[i], i) for i in range(k)]heapq.heapify(q)  # 将列表转换为堆结构,时间复杂度 O(k)# 初始窗口 [0, k-1] 的最大值是堆顶元素的负值(因为存的是负数)ans = [-q[0][0]]  # q[0] 是堆顶(最小的负数 → 对应原数组中最大的正数)# 从第 k 个元素开始,滑动窗口向右移动for i in range(k, n):# 将当前元素 nums[i] 加入堆(同样取负)heapq.heappush(q, (-nums[i], i))# 堆顶可能包含“过期”元素(即索引不在当前窗口 [i-k+1, i] 内)# 当前窗口左边界是 i - k + 1,所以索引 <= i - k 的元素已失效while q[0][1] <= i - k:heapq.heappop(q)  # 弹出堆顶的过期元素# 此时堆顶一定是当前窗口内的最大值(因为其他过期元素已被清理)ans.append(-q[0][0])  # 取负还原为原始最大值return ans

🔍 关键函数与机制说明

1. heapq.heapify(q)
  • 将普通列表 q 转换为最小堆(堆顶是最小元素)。
  • 时间复杂度:O(k)
2. heapq.heappush(q, item)
  • 向堆中插入一个元素,并维持堆性质。
  • 时间复杂度:O(log size)
3. heapq.heappop(q)
  • 弹出并返回堆顶(最小)元素。
  • 时间复杂度:O(log size)
4. 为什么用 (-nums[i], i)
  • Python 没有内置最大堆,但可以通过存储负值,用最小堆模拟最大堆。
  • 例如:原数组 [1, 3, -1] → 存为 [-1, -3, 1],堆顶是 -3,对应原值 3(最大值)。
  • 同时保存索引 i,用于判断该元素是否仍在当前滑动窗口中。
5. 过期元素清理逻辑
  • 滑动窗口范围:[i - k + 1, i]
  • 所以任何索引 ≤ i - k 的元素都不在窗口内,必须被移除。
  • 使用 while 循环持续弹出堆顶,直到堆顶索引有效。

⚠️ 注意:堆中可能残留多个过期元素,但只要堆顶是有效的,就可以保证当前最大值正确。过期元素会在后续被逐步清理。


📊 示例演示

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

初始堆(前3个元素):

  • 存入:[(-1,0), (-3,1), (1,2)]
  • 堆化后堆顶:(-3,1) → 最大值为 3
  • ans = [3]

继续滑动……最终输出:

[3, 3, 5, 5, 6, 7]

⏱️ 复杂度分析

  • 时间复杂度:O(n log n)
    • 每个元素最多入堆一次、出堆一次;
    • 堆操作每次 O(log n),最坏情况下堆大小接近 n。
  • 空间复杂度:O(n)
    • 堆中最多存储 n 个元素。

二、单调队列

下面是对这段使用 双端队列(deque)实现滑动窗口最大值 的 Python 代码的逐行详细注释与解释。这是解决 LeetCode 第 239 题「滑动窗口最大值」的最优 O(n) 解法


class Solution:def maxSlidingWindow(self, nums: List[int], k: int) -> List[int]:n = len(nums)# 使用双端队列 q 存储数组下标(不是值!)# 队列中的下标对应的 nums 值是【单调递减】的# 即:nums[q[0]] >= nums[q[1]] >= ... >= nums[q[-1]]q = deque()# 初始化第一个窗口 [0, k-1]for i in range(k):# 维护单调递减:如果当前 nums[i] >= 队尾对应元素,# 说明队尾元素“永远不可能成为最大值”(因为 i 更靠右且更大/相等),# 所以从队尾弹出这些“无用”元素while q and nums[i] >= nums[q[-1]]:q.pop()# 将当前下标 i 加入队尾q.append(i)# 第一个窗口的最大值就是队首元素对应的值ans = [nums[q[0]]]# 滑动窗口:i 从 k 到 n-1,每次窗口右移一位for i in range(k, n):# 同样维护单调递减队列:移除队尾所有小于等于 nums[i] 的元素while q and nums[i] >= nums[q[-1]]:q.pop()q.append(i)# 检查队首元素是否还在当前窗口 [i - k + 1, i] 内# 如果队首下标 <= i - k,说明它已经滑出窗口左侧,需弹出while q[0] <= i - k:q.popleft()# 此时队首一定是当前窗口内的最大值下标ans.append(nums[q[0]])return ans

🔍 核心思想:单调双端队列(Monotonic Deque)

✅ 为什么用下标而不是值?
  • 需要判断元素是否仍在当前窗口内 → 必须知道其位置(下标)。
  • 通过 q[0] <= i - k 可快速判断队首是否过期。
✅ 为什么队列要保持“单调递减”?
  • 队首始终是当前窗口的最大值下标
  • 对于新加入的元素 nums[i]
    • 如果它比队尾元素大(或相等),那么队尾元素在之后任何窗口中都不可能成为最大值(因为 i 更靠右、存活时间更长、值还不小)。
    • 因此可以安全地从队尾删除这些“被支配”的元素。

🧠 这种策略保证了队列中每个元素都有“成为最大值的机会”,且不会遗漏。


📊 示例演示

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

初始化窗口 [1, 3, -1]

  • i=0: q = [0]
  • i=1: 3 > 1 → 弹出 0,q = [1]
  • i=2: -1 < 3 → q = [1, 2]
  • ans = [nums[1]] = [3]

继续滑动:

  • i=3 (-3):q = [1,2,3] → 队首 1 仍在窗口 → ans += [3]
  • i=4 (5):5 > -3, -1, 3 → 全部弹出,q = [4] → ans += [5]
  • i=5 (3):3 < 5 → q = [4,5] → ans += [5]
  • i=6 (6):6 > 3,5 → 弹出全部,q = [6] → ans += [6]
  • i=7 (7):7 > 6 → 弹出 6,q = [7] → ans += [7]

最终结果:[3, 3, 5, 5, 6, 7]


⏱️ 复杂度分析

  • 时间复杂度:O(n)
    • 每个元素最多入队一次、出队一次,总操作数 ≤ 2n。
  • 空间复杂度:O(k)
    • 队列中最多保存 k 个下标(一个完整窗口)。

✅ 这是理论最优解,比堆(O(n log n))更高效。


💡 关键技巧总结

操作目的
while q and nums[i] >= nums[q[-1]]: q.pop()维护队列单调递减(从队尾删“弱者”)
q.append(i)加入新候选
while q[0] <= i - k: q.popleft()移除过期元素(从队首删“老人”)
ans.append(nums[q[0]])队首即当前窗口最大值
http://www.dtcms.com/a/597290.html

相关文章:

  • TDengine 字符串函数 LENGTH 用户手册
  • Kotlin-协程的挂起与恢复
  • 莱州网站建设有限公司网站页面架构
  • 【Java SE 基础学习打卡】09 JRE 与 JDK
  • 无人机12V锂电池管理控制器方案学习,BQ40Z50
  • React核心概念Mutation
  • 企业建设好一个网站后_如何进行网站推广?网页设计大作业模板
  • VMware Ubuntu 22.04 NAT模式下配置GitHub SSH完整教程(含踩坑实录+报错_成功信息对照)
  • 文生图模型攻击论文原理笔记
  • Goer-Docker系统-1-Dockerfile的构建速度优化
  • 代做网页设计平台站长工具seo综合查询隐私查询导航
  • 方形与圆形滚珠导轨在工业场景如何选型?
  • UCOS-III笔记(一)
  • Unity:lua热更新(一)——AB包AssetBundle、Lua语法
  • 如何在Dev-C++中配置编译选项以支持C++11?
  • 海城区建设局网站快速百度
  • 网站怎样设计网址大全关键词排名提高方法
  • HOT100题打卡第36天——二分查找
  • 【Linux】Linux内存管理与线程控制核心解析
  • dns服务器
  • bash 启动程序的流程
  • 专题:2025中国医疗器械出海现状与趋势创新发展研究报告|附160+份报告PDF、数据、可视化模板汇总下载
  • 工程建设最好的网站石家庄建设网站哪家好
  • c#笔记之面向对象
  • wordpress 移动到回收站发生错误广告公司网络推广计划
  • 汽车乘员热舒适测评的预测模型
  • [6]. SpringAI Alibaba 向量化和向量数据库
  • java学习--包
  • 乐鑫EchoEar开发套件详解:ESP32-S3+端侧AI+全双工语音实战
  • 国外的外贸网站wordpress 页面下文章列表