做滑动窗口之前必须先思考几个问题:
1. 窗口的 "边界" 是什么?
2. 窗口的 "扩大" 和 "收缩" 条件是什么?
- 何时扩大窗口 :一般由
right
指针负责,当窗口内元素不满足条件时(如和小于目标值、未包含所有所需字符),right
右移纳入新元素。 - 何时收缩窗口 :一般由
left
指针负责,当窗口内元素满足条件时(如和大于等于目标值、包含多余字符),left
右移减少元素,试图找到更优解。 - 收缩时用
if
还是while
?- 若只需找到一个可行解后停止收缩(如判断是否存在),用
if
; - 若需在满足条件时持续收缩以寻找最优解(如最短长度),必须用
while
。
- 若只需找到一个可行解后停止收缩(如判断是否存在),用
3. 窗口需要 "维护" 什么信息?
滑动窗口的核心是 "用左右指针维护一个动态区间,通过明确的扩大 / 收缩规则,在一次遍历中找到最优解"。窗口的 "动态平衡"(扩大与收缩的条件)是解题关键。

之前滑动窗口讲述过,当问题涉及连续的子数组、子串或区间,且需要对区间内的元素进行整体处理(如求和、计数、判断是否满足条件)时,我们可以优先使用滑动窗口。
本题的边界就是要求子数组内元素总和大于等于目标值,维护的就是最小满足条件的子数组长度。当子数组总和不满足target,右指针移动,纳入新元素;当子数组总和满足target,左指针移动,并且用while做持续收缩来优化结果。
C++代码:
cpp
class Solution {
public:
int minSubArrayLen(int target, vector<int>& nums) {
int n = nums.size();
int minlen = INT_MAX;
int tar = 0; // 窗口内元素的和
int left = 0; // 左指针(下标)
// 右指针遍历数组,扩大窗口
for (int right = 0; right < n; right++) {
tar += nums[right]; // 将当前元素纳入窗口
// 当窗口和 >= target 时,尝试收缩左指针,寻找最小长度
while (tar >= target) {
int current_len = right - left + 1; // 当前窗口长度
minlen = min(minlen, current_len); // 更新最小长度
// 收缩左指针(从窗口中移除左元素)
tar -= nums[left];
left++;
}
}
// 若未找到符合条件的子数组,返回0
return minlen == INT_MAX ? 0 : minlen;
}
};

本题也是一个滑动窗口的问题,维护的就是一个无重复字符的子字符串区间 ,当窗口内无重复字符时 ,right
右移以纳入新字符,扩大窗口范围。当窗口内出现重复字符时 ,left
右移以移除左侧字符,收缩窗口范围,直至窗口内无重复字符。当 s[right]
与窗口内字符重复时,可能需要连续收缩多次才能移除所有重复影响,所以收缩时应该用while连续收缩。
C++代码:
cpp
class Solution {
public:
int lengthOfLongestSubstring(string s) {
int left = 0;
int maxlen = 0;
int n = s.size();
unordered_set<char> uset;
for (int right = 0; right < n; right++) {
// 若当前字符已在集合中(重复),收缩左窗口并移除离开的元素
while (uset.find(s[right]) != uset.end()) {
uset.erase(s[left]); // 关键:移除左指针指向的元素
left++;
}
// 将当前字符加入集合,更新最大长度
uset.insert(s[right]);
maxlen = max(maxlen, right - left + 1); // 计算当前窗口长度 [left, right]
}
return maxlen;
}
};