news 2026/6/11 9:22:12

【完整题单01、滑动窗口】【✅✅✅✅】

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
【完整题单01、滑动窗口】【✅✅✅✅】

目录

  • 知识框架
  • No.0 筑基
    • 知识框架
    • 滑动窗口核心原理
  • No.1 字符串滑动窗口
    • 题目来源:LeetCode-3. 无重复字符的最长子串
    • 题目来源:LeetCode-438. 找到字符串中所有字母异位词
    • 题目来源:LeetCode-76. 最小覆盖子串
  • No.2 数组滑动窗口
    • 题目来源:LeetCode-209-长度最小的子数组
    • 题目来源:蓝桥杯-第十四届模拟-附近最小

知识框架

No.0 筑基

请先学习下知识点,道友!
题目知识点大部分来源于此:

题目例题大部分来源于此:

知识框架

滑动窗口 =同向双指针
核心思想:用一个指针控制窗口右端点,另一个指针动态调整窗口左端点,把O(n²)暴力解法优化成O(n) 线性解法


滑动窗口核心原理

  1. 暴力解法:两个for循环,枚举所有子区间 → 时间复杂度 O(n²)
  2. 滑动窗口
    • 只用一个for循环遍历窗口右端点right
    • while/条件动态收缩窗口左端点left
    • 全程只遍历一次数组/字符串 →O(n) 效率

一句话口诀:固定右,收缩左,窗口动,结果出


所谓滑动窗口,就是不断的调节子序列的起始位置和终止位置,从而得出我们要想的结果。
所谓滑动窗口,就是不断的调节子序列的起始位置和终止位置,从而得出我们要想的结果。
在暴力解法中,是一个for循环滑动窗口的起始位置,一个for循环为滑动窗口的终止位置,用两个for循环 完成了一个不断搜索区间的过程。
那么滑动窗口如何用一个for循环来完成这个操作呢?
首先要思考 如果用一个for循环,那么应该表示 滑动窗口的起始位置,还是终止位置?
如果只用一个for循环来表示 滑动窗口的起始位置,那么如何遍历剩下的终止位置?
此时难免再次陷入 暴力解法的怪圈。
所以 只用一个for循环,那么这个循环的索引,一定是表示 滑动窗口的终止位置。
那么问题来了, 滑动窗口的起始位置如何移动呢?

No.1 字符串滑动窗口

题目来源:LeetCode-3. 无重复字符的最长子串

题目描述:

题目思路:

题目代码:

classSolution{public:intlengthOfLongestSubstring(string s){// 滑动窗口,感觉像是 同向双指针// 不过滑动窗口更像是 固定一个right,内部while的同时移动left// 虽然像是 同向双指针,但是在固定 一个right之后行为方式更像双端指针了unordered_map<char,int>mp;intn=s.size();intleft=0,right=0;intmaxLen=0;for(right=0;right<n;right++){mp[s[right]]++;while(mp[s[right]]>1){mp[s[left]]--;left++;}maxLen=max(maxLen,right-left+1);}returnmaxLen;}};
classSolution{public:intlengthOfLongestSubstring(string s){intlen=s.size();map<char,int>mp;intans=0;for(inti=0,j=0;j<len;j++){//先判断右边指向的是否在窗口内;//在里面if(mp[s[j]]==1){while(mp[s[j]]==1){mp[s[i]]=0;i++;}}mp[s[j]]=1;ans=max(ans,j-i+1);}returnans;}};
classSolution{public:intlengthOfLongestSubstring(string s){// 滑动窗口,感觉像是 同向双指针// 不过滑动窗口更像是 固定一个right,内部while的同时移动left// 虽然像是 同向双指针,但是在固定 一个right之后行为方式更像双端指针了intn=s.size();if(n==0)return0;unordered_set<char>lookup;intmaxx=0;intleft=0;for(intright=0;right<n;right++){// 开始位置while(lookup.find(s[right])!=lookup.end()){//找到的话 left要左移动了lookup.erase(s[left]);left++;}maxx=max(maxx,right-left+1);lookup.insert(s[right]);}returnmaxx;}};

题目来源:LeetCode-438. 找到字符串中所有字母异位词

题目描述:

题目思路:

感觉和双指针差不多,第一个想到的就是暴力循环的超时的版本
这个是定长的 滑动窗口,就是说 right++ left++ ,然后维持住 使得 这个窗口中的判断尽量超级简单即可

题目代码:

classSolution{public:vector<int>findAnagrams(string s,string p){// p小 s大// 定长滑动窗口 , 如果固定右侧的话// 感觉如果找到第一个,那么后面的就容易了// 搞个数组, 如果两个数组相等就可以了,完美// 然后向右边移动的时候进行 left的对应的减, right对应的加ints_n=s.size();intp_n=p.size();if(p_n>s_n)return{};vector<int>ans_p(26,0);vector<int>ans_s(26,0);for(auto&ch:p)ans_p[ch-'a']++;vector<int>res;intleft=0,right=0;for(right=0;right<s_n;right++){autoch=s[right];ans_s[ch-'a']++;if(right-left+1<p_n)continue;if(ans_s==ans_p){res.push_back(left);}ans_s[s[left]-'a']--;left++;}returnres;}};
classSolution{public:vector<int>findAnagrams(string s,string p){// p小 s大// 定长滑动窗口 , 如果固定右侧的话// 感觉如果找到第一个,那么后面的就容易了// 搞个数组, 如果两个数组相等就可以了,完美// 然后向右边移动的时候进行 left的对应的减, right对应的加std::vector<int>res;intm=s.size();intn=p.size();if(m<n)returnres;std::vector<int>s_v(26,0);std::vector<int>p_v(26,0);for(autoch:p)p_v[ch-'a']++;intleft=0;intright=left+n-1;for(inti=0;i<n;i++){s_v[s[i]-'a']++;}// 定长滑动窗口while(right<m){if(s_v==p_v)res.push_back(left);s_v[s[left]-'a']--;left++;right++;if(right>=m)break;s_v[s[right]-'a']++;}returnres;}};

题目来源:LeetCode-76. 最小覆盖子串

题目描述:

题目思路:

题目代码:

classSolution{public:boolis_cover(vector<int>&a,vector<int>&b){for(inti=0;i<128;i++){if(a[i]<b[i])returnfalse;}returntrue;}stringminWindow(string s,string t){// 一般这种都是滑动窗口// 如果按照之前那个就是 对于t的话每个位置++// 然后是 对于s 进行滑动窗口// 但是怎么滑动呢?// 像这种子串肯定要用 char-'a' 来计数的// 假设现将t进行计数,然后呢就是滑动窗口了,// left, right,固定右边,左侧向右滑动,intt_n=t.size();ints_n=s.size();vector<int>ans_s(128,0);vector<int>ans_t(128,0);for(auto&ch:t)ans_t[ch-'A']++;intleft=0,right=0;intminLen=INT_MAX;intminStart=0;for(right=0;right<s.size();right++){autoch=s[right];ans_s[ch-'A']++;while(is_cover(ans_s,ans_t)){if(right-left+1<minLen){minLen=min(minLen,right-left+1);minStart=left;}ans_s[s[left]-'A']--;left++;}}if(minLen==INT_MAX)return"";returns.substr(minStart,minLen);}};
classSolution{public:boolis_cover(vector<int>&a,vector<int>&b){for(inti=0;i<128;i++){if(a[i]<b[i])returnfalse;}returntrue;}stringminWindow(string s,string t){// 一般这种都是滑动窗口// 如果按照之前那个就是 对于t的话每个位置++// 然后是 对于s 进行滑动窗口// 但是怎么滑动呢?// 像这种子串肯定要用 char-'a' 来计数的// 假设现将t进行计数,然后呢就是滑动窗口了,// left, right,固定右边,左侧向右滑动,std::vector<int>s_vc(128,0);std::vector<int>t_vc(128,0);for(autoch:t)t_vc[ch-'A']++;intm=s.size();intn=t.size();// 处理边界情况,避免越界if(m<n||m==0||n==0){return"";}intleft=0;intright=left+n-1;intminn=0x3f3f3f3f;intminn_start=0;string res="";for(inti=0;i<n;i++)s_vc[s[i]-'A']++;if(is_cover(s_vc,t_vc)){minn_start=left;minn=min(minn,right-left+1);}for(right++;right<m;right++){s_vc[s[right]-'A']++;while(is_cover(s_vc,t_vc)){if((right-left+1)<minn){minn_start=left;minn=min(minn,right-left+1);}s_vc[s[left]-'A']--;left++;}}if(minn==0x3f3f3f3f)returnres;res=s.substr(minn_start,minn);returnres;}};

No.2 数组滑动窗口

题目来源:LeetCode-209-长度最小的子数组

题目描述:

题目思路:

滑动窗口的题目;

题目代码:

classSolution{public:intminSubArrayLen(inttarget,vector<int>&nums){// 很正常的就顺下来了intn=nums.size();intleft=0,right=0;intsum=0;intminLen=INT_MAX;for(right=0;right<n;right++){sum=sum+nums[right];while(sum>=target){minLen=min(minLen,right-left+1);sum=sum-nums[left];left++;}}if(minLen==INT_MAX)return0;returnminLen;}};classSolution{public:intminSubArrayLen(ints,vector<int>&nums){intresult=INT32_MAX;intsum=0;// 滑动窗口数值之和inti=0;// 滑动窗口起始位置intsubLength=0;// 滑动窗口的长度for(intj=0;j<nums.size();j++){sum+=nums[j];// 注意这里使用while,每次更新 i(起始位置),并不断比较子序列是否符合条件while(sum>=s){subLength=(j-i+1);// 取子序列的长度result=result<subLength?result:subLength;sum-=nums[i++];// 这里体现出滑动窗口的精髓之处,不断变更i(子序列的起始位置)}}// 如果result没有被赋值的话,就返回0,说明没有符合条件的子序列returnresult==INT32_MAX?0:result;}};

题目来源:蓝桥杯-第十四届模拟-附近最小

题目描述:

题目思路:

题目代码:

#include<bits/stdc++.h>#defineintlonglongusingnamespacestd;constintN=1e7+10;intq[N];inta[N];intb[N];intn,k;intsum;/* * 滑动窗口是从寻找到的位置i开始算长度的 * 而这个是从i-k开始算的,所以题目可以转换为 * 原数组n+2k个元素,然后从1开始遍历,滑动窗口长度为2*k+1 * 记得初始化前k个元素和后k个元素极大值如1e18,这样就可以防止初值0造成影响 */voidmin_q(){inth=1,t=0;intlen=2*k+1;for(inti=1;i<=n+2*k;i++){while(h<=t&&q[h]+len<=i)h++;while(h<=t&&b[i]<b[q[t]])t--;q[++t]=i;if(i>=len){cout<<b[q[h]]<<" ";}}cout<<endl;}intmain(){cin>>n;for(inti=1;i<=n;i++)cin>>a[i];cin>>k;for(inti=1;i<=k;i++)b[i]=1e18;for(inti=k+n+1;i<=n+2*k;i++)b[i]=1e18;for(inti=k+1;i<=k+n;i++)b[i]=a[i-k];//for (int i = 1; i <= n + 2 * k; i++)cout << b[i] << " ";//cout << endl;min_q();return0;}
版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/6/11 9:21:57

MFC老项目焕新颜:用UDP+CSocket实现轻量级进程间通信(IPC)实战

MFC老项目焕新颜&#xff1a;用UDPCSocket实现轻量级进程间通信&#xff08;IPC&#xff09;实战在维护遗留MFC桌面应用时&#xff0c;开发者常面临一个典型困境&#xff1a;如何在模块化改造过程中实现高效进程间通信&#xff0c;同时避免引入复杂的消息队列或第三方库。我曾参…

作者头像 李华
网站建设 2026/6/11 9:21:56

MC9S12XHZ双核MCU在汽车仪表中的架构解析与实战应用

1. 项目概述&#xff1a;为什么MC9S12XHZ是汽车仪表的“瑞士军刀”&#xff1f;在汽车电子领域&#xff0c;尤其是仪表盘这类需要同时处理图形显示、多路传感器数据、车身网络通信和电机驱动的复杂系统中&#xff0c;选对一颗微控制器&#xff08;MCU&#xff09;往往决定了整个…

作者头像 李华
网站建设 2026/6/11 9:20:52

【JAVA - POI 实战】之 动态生成Word图表:从模板渲染到代码绘制的进阶指南

1. POI操作Word图表的两大核心模式 用Java生成Word文档中的图表&#xff0c;POI库提供了两种截然不同的实现路径。我刚接触这个需求时&#xff0c;也曾纠结该选哪种方案&#xff0c;直到在三个实际项目中踩遍所有坑才真正理解它们的本质差异。 模板填充模式就像给预制房屋刷漆装…

作者头像 李华
网站建设 2026/6/11 9:18:52

C# TcpClient连接状态检测:从Connected属性到实战心跳包方案

1. TcpClient.Connected属性的真相与陷阱 很多C#开发者第一次接触网络编程时&#xff0c;都会天真地以为TcpClient.Connected属性就是判断连接状态的银弹。我当年也是这样踩坑的——在一个物流追踪系统里&#xff0c;用这个属性做在线状态检测&#xff0c;结果半夜收到报警说数…

作者头像 李华