目录
1.⻓度最⼩的⼦数组
滑动窗口的本质就是同向双指针,它也有一个固定的模板,分为进窗口,判断,出窗口,然后再特定的位置更新结果.

解法思路:本题我们使用滑动窗口来解决问题,滑动窗口本质上就是同向的双指针。我们来分析题目,求最短的子数组之和满足大于等于target。我们观察,我们首先定义letf = 0,right = 0 ,将开头定在最左边,right也定义在最左边,然后移动right,求left到right之间的数组之和,当他们之间的和大于等于target的时候,这时的长度有可能时最短长度,所以我们需要与上次(我们定义一开始的最短长度为 INT_MAX)的长度所比较,如果比上一次的短,那么我们就需要更新最短长度。此时我们的left和right之间的数组之和是满足题目要求的,如果我们继续让rihgt++ ,只会是数组更大 ,长度更长,似乎没意义,所以我们需要缩短left和rihgt之间的差距,就要让left++,再次观察left和right之间的数组之和是否满足题目要求。满足我们就可以更新最小长度,如果不满足我们就让rihgt++,如此循环便可以得到最短的长度,这就像一扇窗户一样,在不停的拉开,和关上
代码示例:
class Solution {
public:
int minSubArrayLen(int target, vector<int>& nums) {
int ret = INT_MAX; //定义初始的最小值是INT_MAX 保证第一次比较会改变长度的值
int left = 0,right = 0;
int sum = 0;
while(left<=right && right<nums.size())
{
sum+=nums[right];
while(sum>=target)// 当满足条件时,就跟新长度
{
ret = min(ret,right-left+1);
sum-=nums[left++];
}
right++;
}
return ret==INT_MAX ? 0 : ret;
}
};
2.⽆重复字符的最⻓⼦串

解题思路:
这道题如果用暴力解法的话,就是一个双循环,就可以找到最长的数组,但是这道题目用双循环会超时,所以我们想一下优化的解法,就是滑动窗口。
我们首先定义一个left,和一个right指向字符串的开头,然后让right++,直到right指向重复的字符,我们为了方便可以利用hash表来记录字符出现的次数,当在hash表中有字符出现两次时,我们就需要移动left,并且将left所对应的字符从hash表中去除,也就是出窗口,直到left移动到重复字符的后面,hash表中right所对应的字符就只出现了一次,这是也就说明又没有重复字符了,我们就继续移动right。然后更新最长的长度。
代码示例:
class Solution {
public:
int lengthOfLongestSubstring(string s) {
int hash[128] = {0}; //定义一个仿hash表
int left = 0,right = 0;
int ret = 0;
while(right<s.size()) //结束条件right到达结尾
{
hash[s[right]]++; // 将right所对应的字符对应到hash表中,并且将次数加1;
while(hash[s[right]]>1) // 当right所对应的字符出现两次,就需要出窗口了
{
hash[s[left++]]--; // 出窗口操作,将left所对应的字符在hash表中去除,直到left
//移动到重复字符的后面,此时重复的元素的次数就减去一次,我
//们就可以重新开始寻找没有重复字符的最长字串了
}
ret = max(ret,right-left+1); //更新最大值
right++;// 让right继续下去;
}
return ret;
}
};

解法思路:
不要把这个题目想复杂,可以反转k个整数,不就是在一个全是1的数组中可以有多少了0吗?这样就可以把问题化简。
我们利用滑动窗口算法,定义一个left和一个right指向数据开头,我们可以利用一个计数器count来记录0出现的次数,让right向后移动遇到0的时候就让count++,知道count的值大于k时,我们就需要让left++,然后进行判断,如果left对应是1,就继续让left++,当遇到0时,就让count--,直到count小于等于k,然后更新返回的结果
代码示例:
class Solution {
public:
int longestOnes(vector<int>& nums, int k) {
int count = 0;//计数器
int ret = 0; //返回值
int left =0 ,right = 0; // 左右指针
while(right<nums.size())
{
if(nums[right]==0) // 当right==0 ,计数器++
count++;
while(count>k) // 判断,当count>k时,就需要让left向右移,直到count小于等于k
{
if(nums[left]==0)
{
count--;
}
left++;
}
ret = max(ret,right-left+1);//更新返回值
right++;//右边的指针++;
}
return ret;
}
};
4.将 x 减到 0 的最⼩操作数

解题思路:
这道题目我们发现按照题目的要求这么取做的话,我们用肉眼发现是很简单的,但是代码描述却是十分的复杂,所以我们“正难则反”,我们观察发现这道题目可以变为求最长子数组,使得这个子数组之和等于数组之和(Sum)减去x。当这个子数组最长,并且子数组之和时Sum-x;那么这正好时去除了两边最少的个数并且满足相减为0;这样就简单了,详细解释在代码中
代码示例:
class Solution {
public:
int minOperations(vector<int>& nums, int x) {
int n = nums.size(),Sum = 0;// 将数组长度赋给n,定义Sum记录数组之和
for(auto e: nums)//求数组所有元素之和
{
Sum+=e;
}
int target = Sum - x;// 将Sum-x就是子数组需要满足的条件 定义为target
int len = -1;// 起始长个数为-1;
int left = 0, right = 0; // 利用滑动窗口
int sum = 0;// 这个sum是用来求子数组之和的
if(target<0) //特殊情况:当target<0 也就是整个数组之和都没有x大,那么不存在可以满足的
return -1; //条件,直接返回-1;
while(right<n) // 开始遍历数组
{
sum+=nums[right]; // 将right指向的元素加入sum,求left到right之间子数组的和
while(sum>target)//判断 当sum > target时,说明数组已经过长,并且在left和right之
//间不存在满足的子数组,让right继续向后走没有意义;
{ //所以更新一下子数组,从sum中减去left所指元素,让left++; 这样
// 子数组的个数就更新了
sum-=nums[left++];
}
if(sum==target)//只有当出现sum等于targer时,才能满足条件,然后更新结果
{
len = max(len,right-left+1);//记录下最长数组长度
}
right++;
}
//注意,返回值时,我们求的时最长的子数组但是题目要求时最小的操作次数,所以我们返回值
//时,需要用数组长度减去最长子数组长度
if(len == -1)
return -1;
else
return n-len;
}
};
5.找出字符串中所有字母异位词

这道题目最简单是通过滑动指针和hash表来实现,直接开始题目解析
题目解析:
题目中要求我们找出所有关于p的异位词,异位词就是指p中的所有字符随机组合成的一个新的字符串,我们在这提出一个思路,既然是异位词,那么就包含p中的所有字母,那我们就将题目转化为在s字符串中找到一个子字符串,这个子字符串中的字符数量和字符种类要和p中的要相同就可以了,这样我们就将题目变简单了
详细解释在代码中解释
class Solution {
public:
vector<int> findAnagrams(string s, string p) {
int count = 0//用于计算在窗口中的有效字符数
int hash1[26]={0};//记录p中的对应字符数量
vector<int> ret;//返回的数组
int hash2[26]={0};// 记录在窗口中的对应字符数量
for(auto e: p) //将p中的所有字符和数量记录在hash1中
{
hash1[e-'a']++;
}
//开始查找
for(int left=0, right =0;right<s.size();right++)
{
char in = s[right];//待进窗口字符
hash2[in-'a']++;//进窗口
if(hash2[in-'a']<=hash1[in-'a']) //如hash1中的对应字符数量大于hash2中的对应字
//数(因为是先进了窗口,所以要加上等于),如何是
// 一个无效字符,他一定不在hash1中,所以我们就
//不需要将有效位数量count++,他在最后出窗口时也
//一定会被出掉的
{
count++;//有效字符就加1,
}
if(right - left +1 > p.size()) //判断 当right和left之间的值大于p的数量时,就
////代表窗口中的元素过多,需要出窗口
{
char out = s[left]; //待出窗口元素
if(hash2[out-'a'] <= hash1[out-'a']) //出窗口 当待出窗口元素数量小于等于
//hash1中的元素时我们就要将该元素从有
//效字符数量中减去,然后出窗口
count--;
hash2[out-'a']--;
left++;
}
if(count==p.size()) //当此时我们的有效字符数量和p的字符数量相等的时候,说
//明我们找到了一个异位词,将起始位置left翻入ret中
{
ret.push_back(left);
}
}
return ret;
}
};
6.水果成篮

题目解析:
我们经过读题可以发现,题目要求我们的两个篮子里中的每个篮子的水果种类要是相同的,也就是我们所能采摘的水果只能是两种,所以我们就将题目转化为了在一个数组中,只有两种数字的最大长度,显然,这题的解题思路还是滑动窗口
代码示例:
class Solution {
public:
int totalFruit(vector<int>& fruits) {
unordered_map<int,int> hash;//用一个hash表来记录种类和数量
int ret = 0;
for(int left = 0,right =0 ;right<fruits.size();right++)
{
hash[fruits[right]]++;//进窗口 将该种类放入hash表中
if(hash.size()>2)//判断如果种类大于两种了,就需要出窗口
{
while(hash[fruits[left]] >1) //如果当前品种的数量大于1,我们就需要将他减去,并
//且将他从窗口里出掉
{
hash[fruits[left]]--;//减去一个数量
left++;//出窗口
}
hash.erase(fruits[left]);//到这里的时候,种类数量还多余两个,此时left所指向
//的种类的个数一定只剩一个了,所以我们需要将他从
////hash表中去除
left++;
}
ret = max(right-left+1,ret);//更新结果
}
return ret;
}
};
7.最小覆盖字串

题目解析:
这道题目要求我们找出s中涵盖t所有字符的最小字串,并且在这个最小子串中各种字符的数量必须是大于等于t中的对于字符的数量,并且最小的字串只有一个。对于最小字串的题目,显然还是我们的滑动窗口算法。我们可以利用 例5 中的思路来解决这道题目,具体解法思路放在代码中。
代码示例:
class Solution { //与本篇博客第五题思路类似
public:
string minWindow(string s, string t) {
int hash1[128] = { 0 };//统计t中的字符出现数量,为了提高效率不使用STL容器
int kinds = 0;//用于统计t中的字符种类
for(auto e : t)//统计t中的字符数量以及字符种类
{
if(hash1[e]==0) kinds++;//当我第一次遇见这个字符就将kinds加1
hash1[e]++;//对于字符数量加1
}
int minlen = INT_MAX; int begin = -1;//用于返回,记录最小长度以及起始位置
int hash2[128] = { 0 };//记录窗口中的个字符数量
for(int left = 0, right = 0,count = 0;right<s.size();right++)//用count来记录有效的
//字符种类,注意这里与例5不同的是不再是记录
//数量,而是以及满足了的字符种类数量(看不懂
//继续往下看)
{
char in = s[right]; //进窗口
hash2[in]++;//对于元素再hash2中+1
if(hash2[in] == hash1[in]) count++;//注意 我们只有当hash2中的元素数量与hash1中的
//对应元素数量相同时,说明这时窗口内的该元
//素数量达到了要求,所以我们让count++,说明满足了其中一个元素
//判断
while(count==kinds)//当count的种类数量和t中的字符种类数量相同的时候,说明此时是一个
//满足条件的窗口,这时我们需要更新结果,然后出窗口,继续寻找下一
//个满足条件的范围区间
{
if(right-left+1 < minlen)//当此时的区间范围下雨minlen时更新
{
minlen=right-left+1;
begin = left;
}
char out = s[left];//出窗口
left++;
if(hash2[out]==hash1[out]) count--;// 如果此时出窗口的元素的数量和t中对应元素
//的数量是相等的,将此元素移除后,该元素的
//数量就不再符合要求,所以count就需要-1
hash2[out]--;
}
}
if(begin==-1) return "";//返回结果,如果起始位置没有变化说明没有符合的条件,返回空
//字符串,起始位置发生变化,则返回s的字串。
else return s.substr(begin,minlen);
}
};

5470

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



