LeetCode-滑动窗口
🧠 通过移动 right 和 left,始终维护一个 [left, right] 的连续区间,并根据题目条件不断扩大或缩小这个区间。
一句话结论:
👉 right 进窗(for 循环控制只增不减) left 出窗(只在 while (满足条件) 时移动只增不减)
注意窗口状态,遇到出现次数与不能重复联想哈希表 set(元素不能重复) 和 map(Key 不能重复,Value 可以重复)
⭐ 模板
初始化
left = 0 (窗口的左边界)
res = 初始值 (代表的全局最优解,求最大值可以刚开始设为 0,最小值设为无穷大)
windows = {} (初始化窗口状态[Nt4])窗口内数据处理(控制窗口右边界主动向右边扩张)
for(int right = 0; right < nums.length; right++){
//1.把元素添加进窗口
window.add(nums[right]);//2.判断窗口是否合法 【出】如果不满足条件,left 被迫移动(范围变小)
while(导致 left 必须右移的原因)
{
//3.把元素移出窗口
window.remove(nums[left]);
left++;
}//4.判断定长还需要检查结果 (if)
if(满足条件){}
//更新 res[Nt2]
}返回值
return res;
Notice
- 控制 left 的移动只可以使用 while 不可以使用 if
- res 更新位置
找最小 :在 for 里面 while 里面更新,要在 left++之前更新。
找最大 :在 for 里面 while 外面更新。
对于定长的滑动窗口:必须写在 “刚加进第 right 个元素” 之后,且 “还没踢掉第 left 个元素” 之前
- Map 的“尸体”清理 (Remove Key)
做计数类题目(如 904 水果成篮)时,最容易忘的一步。
现象:map.get(key) 已经减到 0 了,但 map.size() 还是没变。
后果:会导致 while (map.size() > 2) 变成死循环,或者逻辑完全错误。
口诀:“减到 0 必须 remove”。
1 | map.put(outVal, map.get(outVal) - 1); |
- Windows 初始化常见的四种状态
[是字符统计就用数组 int[];是数字统计就用 Map;只查重复就用 Set;只求数值和就用 int。]
- 数组 int[] (字符串题首选)
适用场景:题目只包含字符(如 s 包含小写字母、ASCII 码)。
作用:统计字符出现的频率(次数)。
// 覆盖所有 ASCII 字符
int[] window = new int[128];
// 或者只覆盖小写字母 (需配合 char - ‘a’ 使用)
int[] window = new int[26];
Tips:数组的函数:
1 | 判断数组是否相同(元素,顺序,个数): Arrays.equals(arr1, arr2) |
- 哈希表 Map (通用型)
适用场景:元素不是字符(比如数组里的数字),或者数字很大、很稀疏(如 [-10^9, 10^9])。
作用:统计元素出现的频率,或记录索引。
Map<Integer, Integer> window = new HashMap<>();
- 哈希集合 Set (判重型)
适用场景:不需要统计“出现了几次”,只需要知道“有没有重复”。
作用:快速查找是否存在。最经典题目:无重复字符的最长子串。
Set
- 简单变量 int (数学型)
适用场景:题目考察的是数值计算,如“子数组之和”。
作用:累加器,不需要存具体元素,只存总数。
int currentSum = 0; // 记录窗口内的和
- 求和+滑动窗口
要求数组里面不可以有负数,负数破坏了“越加越大”的规律,while 无法判断该缩还是该扩。
相关练习题
1. LeetCode 904. 水果成篮
题意: 你正在探访一家农场,农场从左到右种植了一排果树。这些树用一个整数数组 fruits 表示,其中 fruits[i] 是第 i 棵树上的水果 种类 。
你想要尽可能多地收集水果。然而,农场的主人设定了一些严格的规矩,你必须按照要求采摘水果:
你只有 两个 篮子,并且每个篮子只能装 单一类型 的水果。每个篮子能够装的水果总量没有限制。
你可以选择任意一棵树开始采摘,你必须从 每棵 树(包括开始采摘的树)上 恰好摘一个水果 。采摘的水果应当符合篮子中的水果类型。每采摘一次,你将会向右移动到下一棵树,并继续采摘。
一旦你走到某棵树前,但水果不符合篮子的水果类型,那么就必须停止采摘。
给你一个整数数组 fruits ,返回你可以收集的水果的 最大 数目。
Tips:哈希表里的计数神器:
(哈希表里的计数神器:map.getOrDefault(key, defaultValue),key 是要查的元素,defa 是出现次数,但是他不会算上现在这一次,只查过去所以+1)
1 | int inFruit = fruits[right]; |
2. LeetCode 567. 字符串的排列
题意: 给你两个字符串 s1 和 s2 ,写一个函数来判断 s2 是否包含 s1 的 排列。如果是,返回 true ;否则,返回 false 。换句话说,s1 的排列之一是 s2 的 子串 。
3. LeetCode 76. 最小覆盖子串
题意: 给定两个字符串 s 和 t,长度分别是 m 和 n,返回 s 中的 最短窗口 子串,使得该子串包含 t 中的每一个字符(包括重复字符)。如果没有这样的子串,返回空字符串 “”。

