文章目录
[将 x 减到 0 的最小操作数](#将 x 减到 0 的最小操作数)
前言
接上一篇的算法内容,这篇来讲解"滑动窗口"。
介绍
滑动窗口是算法中基于同向双指针的高效方法,通过左右两个指针在数组、字符串等结构上同向移动,形成一个 "窗口"。它通过动态调整窗口的左右边界,在 O (n) 时间复杂度内解决子数组、子串的统计类问题,如最长无重复子串、最大子数组和等;它也避免了暴力枚举的高复杂度。其核心是利用同向指针的协同移动,高效维护窗口的有效范围以完成统计或优化目标。
长度最小的子数组
1.题目解析
这道题是中等难度的题目,看完题目结合例子来分析这道题的要求。
画图来分析:题目要求需要一个数组里的元素之和大于等于7且长度是最小的;从上图可看出,有两组数组的元素之和大于或者等于7,但两组长度不一样,显而易见第二组数组满足条件,则该例题从原则上是数组[ 4, 3 ] 满足条件。
2.算法原理
通常最优的解法基本上是通过暴力解法进行优化得出,此题我将直接采用最优解法------滑动窗口。
由自己画图来分析,我们可以发现一直往后面遍历,满足条件的小数组就越多而长度也在增加;因此我们可以设定在得到第一个满足条件的小数组就更新结果,并让left++
只要出现满足条件的数组,就让left++;相当于right用来扩展,left用来收缩
3.编写代码
cpp
class Solution {
public:
int minSubArrayLen(int target, vector<int>& nums) {
int n =nums.size(),sum=0,len=INT_MAX;//INT_MAX 是一个预定义的常量
for(int left=0 ,right=0;right<n;right++)
{
sum+=nums[right];//更新结果
while(sum>=target)//进窗口
{
len=min(len,right-left+1);//计算当前有效窗口的长度(right - left + 1),与 len 比较取最小值,更新 len
sum-=nums[left++];//left 右移一位(收缩窗口)
}
}
return len == INT_MAX?0:len;//如果没有找到>=target的数据就返回0;找到就返回len
}
};
扩展知识点:
INT_MAX是一个预定义的常量。
INT_MAX的核心功能是标记 int类型的最大值边界,常见于:
- 初始化需找「最小值」的变量;
- 表示「未找到目标」的无效状态;
- 数值运算中的溢出判断。
无重复字符的最长子串
1.题目解析
老样子,做题前,咱们一定要先看题分析再动手;并且亲自手动画图来分析例子,题目要求返回最长子串的长度
2.算法原理
看了上题,我们可以大概明白很多算法题都可以进行暴力解法,只是暴力解法不算最优解法;只要通过分析我们发现可以利用"同向双指针"的原理,来判断一道算法题是否能使用"滑动窗口"来解答。重点就在于:left=0,right=0,这样两指针才能同方向前进。
本题要结合hash表来判断字符重复出现 (如果对hash表不明白,可以去查阅一下资料或者相关的博客,(^^))
先让right开始走,left不变,往前走就把字符带进hash表中记录进去
没有出现重复的字符就一直往前走并记录结果;当出现重复的字符时,第二次出现的字符已经进入hash表中,就要先hash--,把重复的字符踢出去再让原先大子串中left++
让记录进去的结果进行比较,最大的子串就出来了;最后right++,并返回结果
3.编写代码
cpp
class Solution {
public:
int lengthOfLongestSubstring(string s) {
int hash[128]={0}; //创建一个数组用来模拟hash表,这样不用创建一个hash表
//第一层用for/while循环都可以
//练手,我用while
int left=0,right=0,n=s.size();
int ret=0;
while(right<n)
{
//进窗口就是进入hash表,用hash表来判断
hash[s[right]]++;
//判断是否出现重复字符和是否出窗口
while(hash[s[right]] > 1) //这样大于1的意思是:如当数组中a出现2次,就判断检查的数组中有重复字符出现
{
hash[s[left++]]--; //先--再++ ; 先让左边第一个出来,再让left++
}
ret=max(ret,right-left+1);
//闭区间长度的计算规则:终点索引 - 起点索引 + 1(比如 left=0、right=2 时,窗口是 [0,1,2],长度是 2-0+1=3,而非 2);
//C++ 中的库函数,作用是比较 a 和 b,返回更大的那个值
right++;
}
return ret;
}
};
将 x 减到 0 的最小操作数
1.题目解析
看问题,明白题目的要求,开始画图分析
2.算法原理
结合上面俩个题目的解法,我们可以归纳成4个步骤:
1.left=0,right=0
2.进窗口
3.判断什么时候出窗口
4.更新结果
3.编写代码
cpp
class Solution {
public:
int minOperations(vector<int>& nums, int x) {
//先求数组元素之和
int sum=0,ret=-1; //取-1是因为不满足条件就返回-1
//用范围for
for(int a:nums)
sum+=a;
int target=sum-x; //需要的最长数组元素之和
if(target<0) //细节处理
return -1;
for(int left=0,right=0,tmp=0;right<nums.size();right++) //用tmp记录[left,right]元素之和
{
//进窗口
tmp+=nums[right];
//判断
while(tmp>target) //循环遍历
tmp-=nums[left++];
if(tmp==target)
ret=max(ret,right-left+1); //取子数组的长度
}
if(ret==-1)
return ret;
else return nums.size()-ret; //数组长度-子数组的长度
}
};
找出字符串中所有字母异位词
1.题目解析
看题目并结合例子来理解题目要求
2.算法原理
先用两个hash表来判断两个字符串是否是"异位词",
hash1用来记录字符串p中的字符,hash2用来记录字符串s中的字符,
比较两个hash表中的字符是否出现相同的字母
结合滑动窗口+hash表的方法来解决问题
1.先确定同向的双指针:left = 0 , right = 0
2.进窗口的方式:hash2[ int ]++,从左边开始进入,就开始计数 count ++
3.判断出窗口的条件:hash2表记录的字符串长度只能等于hash1表字符串的长度
4.出窗口的方式:hash2[ out ]-- , 从左边开始删除字符,在出窗口前就 count--
5.更新结果
3.编写代码
cpp
class Solution {
public:
vector<int> findAnagrams(string s, string p) {
//滑动窗口+hash表
//题目要求结果以数组类型返回
vector<int> ret;
//用hash1把p中的字符串记录下
int hash1[128] = {0};
for(auto ch :p)
hash1[ch-'a']++; //题目要求是小写字母
//用hash2来查找符合的子串
int hash2[128]={0};
int m=p.size();
for(int left=0,right=0,count=0;right<s.size();right++)
{
//进窗口
char in=s[right];
if(++hash2[in-'a'] <= hash1[in-'a'])
count++; //记录字符
//出窗口
if(right-left+1 > m)
{
char out=s[left++];
if(hash2[out-'a']-- <= hash1[out-'a'])
count--; //字符类型不能自增和自减,不要写成out--
}
//更新结果:是跟进出窗口一起的
if(count==m)
ret.push_back(left); //要返回下标数
}
return ret;
}
};
总结
非常感谢大家阅读完这篇博客。希望这篇文章能够为您带来一些有价值的信息和启示。如果您发现有问题或者有建议,欢迎在评论区留言,我们一起交流学习。













