目录
引言:
滑动窗口是处理数组/字符串子区间问题的经典技巧。本文将通过三道 LeetCode 高频题,由浅入深地讲解双指针滑动窗口的核心思想与应用技巧。
| 题目 | 核心技巧 |
|---|---|
| 长度最小的子数组 | 正数单调性 → 双指针收缩求最值 |
| 无重复字符的最长子串 | 哈希判重 → 动态维护合法窗口 |
| 最大连续 1 的个数 III | 状态标记 → 转化约束条件 |
209. 长度最小的子数组

题目分析
给定一个含有 n 个正整数的数组和一个正整数 target,找出该数组中满足其和 ≥ target 的长度最小的连续子数组,并返回其长度。如果不存在符合条件的子数组,返回 0。
逻辑梳理
由于数组元素均为正数,子数组的和具有单调性——窗口扩大时和增加,缩小时和减少。这一性质使得双指针滑动窗口成为天然的选择:
- 维护窗口
[l, r),其中l为起始位置,r为结束位置的下一个位置(左闭右开) ret记录当前窗口内元素之和- 若
ret < target,说明窗口还需扩大,r右移并将新元素加入和 - 若
ret >= target,说明当前窗口满足条件,更新最小长度,随后尝试收缩窗口:l右移并将离开窗口的元素从和中扣除 - 当
r到达数组末尾后,当前窗口可能仍满足条件,需在外层继续收缩并更新答案
代码实现
class Solution {
public:
int minSubArrayLen(int target, vector<int>& nums) {
int len = 0x3f3f3f3f;
int l = 0,r = 1;
long long ret = nums[0];
while(r<nums.size())
{
if(ret<target)
{
ret+=nums[r++];
}
else
{
len = min(r-l,len);
ret-=nums[l++];
}
}
while(ret>=target)
{
len = min(r-l,len);
ret-=nums[l++];
}
return len==0x3f3f3f3f?0:len;
}
};
复杂度分析
- 时间复杂度:O(N),每个元素最多被
l和r各访问一次 - 空间复杂度:O(1),仅使用常数个额外变量
3. 无重复字符的最长子串

题目分析
给定一个字符串 s,找出其中不含有重复字符的最长子串的长度。
逻辑梳理
本题与上一题思路一脉相承,核心差异在于窗口的合法性条件由"和 ≥ target"变为"无重复字符"。借助哈希表(或数组)记录字符出现次数,即可 O(1)判断重复:
st[200]作为字符频次数组(ASCII 范围足够覆盖常见字符)l指向当前无重复子串的起始位置- 遍历右端点
i,若s[i]已存在于窗口中,则不断右移l直至s[i]不再重复 - 每次将
s[i]纳入窗口后,更新最大长度
代码实现
class Solution {
public:
int lengthOfLongestSubstring(string s) {
int st[200]{};
int l = 0,ans = 0;
for(int i =0; i<s.size();i++)
{
while(st[s[i]])
{
st[s[l++]]--;
}
st[s[i]]++;
ans = max(ans,i-l+1);
}
return ans;
}
};
复杂度分析
- 时间复杂度:O(N),左右指针均单向移动,每个字符最多被访问两次
- 空间复杂度:O(1),固定大小的频次数组,与输入规模无关
1004. 最大连续1的个数 III

题目分析
给定一个由若干 0 和 1 组成的数组 nums 以及整数 k,最多可以将 k 个 0 翻转为 1,返回最长的连续 1 的子数组长度。
逻辑梳理
本题是滑动窗口的变体应用:将"最多翻转 k 个 0"转化为"窗口内 0 的个数不超过 k"。为了优雅地处理翻转状态的回退,采用一个技巧——将翻转过的 0 标记为 2:
l初始化为-1(窗口起始前一个位置),r为窗口结束位置- 遇到
nums[r] == 1:直接扩展窗口,更新答案 - 遇到
nums[r] == 0且还有剩余翻转次数k:将其翻转为 2,k--,更新答案 - 遇到
nums[r] == 0但无剩余次数:右移l直至遇到第一个被翻转的 2,将其恢复为 0(k++的等价操作),随后将当前r位置的 0 翻转为 2。此过程中窗口长度不变,仅需继续右移r
代码实现
class Solution {
public:
int longestOnes(vector<int>& nums, int k) {
int ans = 0;
int l = -1,r = 0,tmp = k;
while(r<nums.size())
{
if(nums[r]==0)
{
if(k)
{
nums[r]=2;
k--;
ans = max(ans,r-l);
}
else
{
while(k==0&&l<r)
{
l++;
if(nums[l]==2)
{
nums[r] = 2;
break;
}
}
}
}
else if(nums[r]==1)
{
ans = max(ans,r-l);
}
r++;
}
return ans;
}
};
复杂度分析
- 时间复杂度:O(N),
l和r均最多遍历数组一次 - 空间复杂度:O(1),原地修改数组,未使用额外数据结构
结语:
掌握滑动窗口的关键在于:识别问题的单调性,明确定义窗口的合法性条件,并保证双指针的单向移动。当遇到子数组/子串最值问题时,不妨优先考虑这一利器。
希望以上内容对你有所帮助,感谢观看,若觉得写的还可以,可以分享给朋友一起来看哦,毕竟一起进步更有动力嘛,当然能关注一下就更好啦。


30

被折叠的 条评论
为什么被折叠?



