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

LeetCode 高频题解:滑动窗口与双指针的通用解题框架

LeetCode 高频题解:滑动窗口与双指针的通用解题框架

一、滑动窗口的直觉与误区:不是所有连续子数组都适用

滑动窗口是 LeetCode 高频考点,但很多初学者的理解停留在"左右指针移动"的表面,遇到变形题就无从下手。核心误区有两个:第一,认为滑动窗口只适用于"连续子数组"问题,实际上只要满足"窗口内状态可增量更新"的条件,非连续场景也能用;第二,不知道何时用定长窗口、何时用变长窗口,导致框架选错。

滑动窗口的本质是:用 O(1) 或 O(log n) 的代价维护窗口内的状态,避免对每个子数组重新计算。关键判断标准是"窗口收缩时状态能否快速更新"——如果每次收缩都需要 O(n) 重建状态,滑动窗口就不比暴力解法更优。

二、滑动窗口的分类与框架

graph TB SW[滑动窗口] --> FIXED[定长窗口 大小固定] SW --> VAR[变长窗口 大小可变] VAR --> MIN[最小窗口 满足条件的最短] VAR --> MAX[最大窗口 满足条件的最长] FIXED --> AVG[滑动平均/最大值] FIXED --> FREQ[频率统计] MIN --> FRAME1[框架:右扩左缩 找最短] MAX --> FRAME2[框架:右扩左缩 找最长]

三、通用解题框架与经典题型

from collections import defaultdict, Counter from typing import List # ========== 框架 1:变长窗口 — 最小覆盖子串 ========== # LeetCode 76. Minimum Window Substring def min_window(s: str, t: str) -> str: """最小覆盖子串:找 s 中包含 t 所有字符的最短子串""" if not s or not t or len(s) < len(t): return "" need = Counter(t) # 需要的字符频率 window = defaultdict(int) # 窗口内的字符频率 valid = 0 # 满足要求的字符数 left = 0 min_len = float("inf") result = "" for right, ch in enumerate(s): # 右扩:将字符加入窗口 window[ch] += 1 if ch in need and window[ch] == need[ch]: valid += 1 # 左缩:当窗口满足条件时,尝试收缩 while valid == len(need): # 更新最短结果 if right - left + 1 < min_len: min_len = right - left + 1 result = s[left:right + 1] # 移除左端字符 left_ch = s[left] if left_ch in need and window[left_ch] == need[left_ch]: valid -= 1 window[left_ch] -= 1 left += 1 return result # ========== 框架 2:变长窗口 — 最长无重复子串 ========== # LeetCode 3. Longest Substring Without Repeating Characters def length_of_longest_substring(s: str) -> int: """最长无重复子串""" window = set() left = 0 max_len = 0 for right, ch in enumerate(s): # 左缩:直到窗口内无重复 while ch in window: window.remove(s[left]) left += 1 window.add(ch) max_len = max(max_len, right - left + 1) return max_len # ========== 框架 3:定长窗口 — 滑动最大值 ========== # LeetCode 239. Sliding Window Maximum from collections import deque def max_sliding_window(nums: List[int], k: int) -> List[int]: """定长滑动窗口最大值:单调队列 O(n)""" dq = deque() # 存储索引,保持对应值单调递减 result = [] for i, num in enumerate(nums): # 移除超出窗口的元素 while dq and dq[0] <= i - k: dq.popleft() # 移除比当前元素小的(它们不可能成为最大值) while dq and nums[dq[-1]] < num: dq.pop() dq.append(i) # 窗口形成后,队首就是最大值 if i >= k - 1: result.append(nums[dq[0]]) return result # ========== 框架 4:双指针 — 对撞指针 ========== # LeetCode 11. Container With Most Water def max_area(height: List[int]) -> int: """盛最多水的容器:对撞指针,每次移动较矮的指针""" left, right = 0, len(height) - 1 max_water = 0 while left < right: width = right - left h = min(height[left], height[right]) max_water = max(max_water, width * h) # 移动较矮的指针:只有换更高的柱子才可能增加面积 if height[left] < height[right]: left += 1 else: right -= 1 return max_water # ========== 框架 5:双指针 — 快慢指针 ========== # LeetCode 141/142. Linked List Cycle class ListNode: def __init__(self, val=0, next=None): self.val = val self.next = next def detect_cycle(head: ListNode) -> ListNode: """检测链表环的入口:快慢指针 + 数学推导""" slow = fast = head # 第一阶段:快慢指针相遇 while fast and fast.next: slow = slow.next fast = fast.next.next if slow == fast: break else: return None # 无环 # 第二阶段:一个指针从头部出发,与慢指针相遇即为环入口 # 数学证明:a = (n-1)(b+c) + c,其中 a 为头到入口,b 为入口到相遇,c 为相遇到入口 ptr = head while ptr != slow: ptr = ptr.next slow = slow.next return ptr

四、滑动窗口与双指针的 Trade-offs 分析

窗口状态维护的复杂度:字符频率统计用 Counter 即可,但如果窗口状态是"窗口内元素之和"或"窗口内最大值",需要不同的数据结构。求和用前缀和 O(1),求最大值用单调队列 O(1) 均摊,求中位数需要两个堆 O(log n)。

定长 vs 变长的选择:题目明确窗口大小用定长,否则用变长。变长窗口的难点在于"何时收缩"——需要明确定义"窗口满足条件"的判断标准。如果条件难以增量判断(如"窗口内元素互质"),滑动窗口可能不适用。

双指针的适用范围:对撞指针要求数组有序或具有单调性,快慢指针适用于链表和数组去重。双指针不能替代所有 O(n²) 枚举——如果问题不满足单调性,双指针无法保证正确性。

边界条件的处理:空输入、单元素、全相同元素是常见的边界陷阱。建议每个框架都包含空输入检查,并在窗口初始化时正确处理第一个元素。

五、总结

滑动窗口和双指针是 LeetCode 高频考点,核心是"用增量更新替代全量重算"。变长窗口分"最小覆盖"和"最长无重复"两种框架,定长窗口配合单调队列处理最值问题。双指针分对撞指针和快慢指针两类,分别适用于有序数组去重和链表环检测。掌握框架后,关键训练点是"窗口状态维护"的数据结构选择和"收缩条件"的精确定义。

http://www.jsqmd.com/news/1013877/

相关文章:

  • 半导体工艺参数优化:用贝叶斯优化替代试错法
  • 2026年浪琴全国售后网络全新升级(最新服务热线与网点地址汇总) - 资讯速览
  • MTK8088单板机制作(二)激活测试
  • 2026青岛奢侈品回收口碑老店 正规商家盘点 - 资讯速览
  • Java+AI全栈工程师新一代技术人才的进化之路
  • 解锁Dify工作流魔法:零代码打造小红书爆款卡片
  • 2026年6月最新版喀什正规房屋漏水防水补漏维修口碑名单:创维修缮机构等5家深度测评 - 一修哥咨询
  • 2026年6月最新版晋中正规房屋漏水防水补漏维修口碑名单:创维修缮机构等5家深度测评 - 一修哥咨询
  • 2026年6月最新版黄冈正规房屋漏水防水补漏维修口碑名单:创维修缮机构等5家深度测评 - 一修哥咨询
  • 2026上海律所办公室装修:专业合规适配与服务商适配深度解析 - 资讯速览
  • 京东物流和德邦哪个便宜?寄大件快递这样选最省钱 - 快递物流资讯
  • 2026电商流量转化导师中立测评榜单|全域付费自然流量选型指南 - 品牌2026推荐
  • 多节点访问轮询算法:从基础到实战
  • 5000+戴森球计划工厂蓝图:从新手到专家的完整建造指南
  • 水机制动屏ZDK-15组合电磁空气阀
  • 索尼相机推荐哪个品牌的卡 - 资讯速览
  • 如何5分钟掌握AMD Ryzen处理器深度调试:免费开源工具终极指南
  • 如何快速掌握博德之门3模组管理:BG3ModManager完整教程
  • 2026年6月最新版丽江正规房屋漏水防水补漏维修口碑名单:创维修缮机构等5家深度测评 - 一修哥咨询
  • 2026资源型EMBA客观测评:高管理性择校全指南 - 品牌2026推荐
  • CST中优化器中优化算法介绍
  • 调查研究-175 Supermemory:AI 时代的 Memory API,不只是另一个向量数据库
  • Mac Mouse Fix终极指南:让普通鼠标在macOS上媲美触控板的免费神器
  • 2026别被大牌溢价忽悠!深圳全屋定制新品牌“源木匠心”深度测评与真实案例揭底
  • C# 生成命令行程序 将hex格式烧录程序转换成bin烧录格式
  • IWC万国表专业维修养护指南:守护沙夫豪森的精密时光 - 资讯速览
  • 数据类型转换:隐式转换与强制转换实战(避坑全覆盖)
  • 2026年6月最新版克拉玛依正规房屋漏水防水补漏维修口碑名单:创维修缮机构等5家深度测评 - 一修哥咨询
  • 终极指南:5步快速掌握Beat Saber模组管理神器ModAssistant
  • 社交行为与语言特征联合建模识别抑郁风险