滑动窗口本质是双指针维护区间,用left和right控制子数组边界;核心是右扩左缩,关键在“何时缩”;慎用vector(256)频次数组,优先unordered_map;while中避免混用++i和i++,需拆分操作;注意边界初始值与终止条件。

滑动窗口本质是双指针维护区间
滑动窗口在 C++ 里不是某个现成函数,而是用两个 int 变量(比如 left 和 right)控制一段连续子数组/子串的边界。核心动作就两个:右指针扩张扩大窗口、左指针收缩缩小窗口。关键不在于“怎么写”,而在于“什么时候缩”——这由题目约束决定,比如「最多包含 2 个不同字符」「和不超过 target」。
常见错误是把 right 写成 for (int right = 0; right 后,在循环内无条件移动 <code>left,结果漏掉某些合法窗口或重复计算。正确做法是:每次 right 移动后,用 while 循环检查约束是否被破坏,只在必要时推进 left。
用 unordered_map 记录窗口内元素频次最常用
当题目涉及「字符种类数」「重复次数限制」(如 LeetCode 3、76、209),unordered_map<char int></char> 或 unordered_map<int int></int> 是最直接的选择。它支持 O(1) 插入和查找,但要注意:每次 left 移动时,必须显式更新 map ——删完一个元素后如果计数归零,得调用 erase(),否则 map.size() 会虚高。
-
right进来:执行freq[s[right]]++ -
left出去:执行freq[s[left]]--,再判断if (freq[s[left]] == 0) freq.erase(s[left]); - 别依赖
freq.count(x)判断是否存在,它返回 true 即使值为 0;应查freq[x] > 0或先erase
数组题慎用 vector(256) 替代哈希表
遇到只含 ASCII 字符的字符串题(如小写字母),有人图快用 vector<int> count(256)</int> 当频次数组。它确实更快、无哈希开销,但有硬伤:
立即学习“C++免费学习笔记(深入)”;
- 下标越界风险:若输入含
\0或非打印字符,s[i]直接当索引可能崩,得确保static_cast<unsigned char>(s[i])</unsigned> - 内存浪费:256 个 int 是 1KB,对嵌入式或极端空间题不友好
- 可读性差:看到
count[127]不知道对应哪个字符,调试时不如freq['a']直观
除非明确要求极致性能且输入范围绝对干净,否则优先用 unordered_map。
while 循环里别混用 ++i 和 i++
写收缩逻辑时,容易在 while 条件里写 left++ 导致逻辑错位。例如:
while (sum > target) sum -= nums[left++]; // ❌ left 先自增,再减,nums[left] 拿错了
应该拆开:
while (sum > target) {
sum -= nums[left];
left++;
}同理,right 扩张也别写成 sum += nums[right++],尤其当后续还要用 right 下标做其他操作时,自增时机一错,整个窗口偏移。
边界处理永远是最容易被跳过的部分:left 和 right 的初始值、循环终止条件( 还是 <code>)、窗口长度计算(<code>right - left + 1 还是 right - left)——这些都得对照具体题干里的「子数组」「子串」「至少/至多」字眼逐字确认。










