专题二_滑动窗口_算法专题详细总结
目录
滑动窗口,引入:
滑动窗口,本质:就是同向双指针;
1.⻓度最⼩的⼦数组(medium)
1.解析:给我们一个数组nums,要我们找出最小子数组的和==target,首先想到的就是暴力解法
1)暴力:
2)优化,滑动窗口:
1.进窗口
2.出窗口
3.更新值
2.⽆重复字符的最⻓⼦串(medium)
1)仍然是暴力解法:
2)优化:
进窗口:hash[s[right]]++;
出窗口: while(hash[s[right]]>1) hash[s[left++]]--;
更新len: right-left+1;
3.最⼤连续1的个数III(medium)
1)暴力:
2)优化:
进窗口:if(nums[right]==0) _k++;
出窗口:while(_k>k) if(nums[left++]==0) _k--;
更新len:len=max(len,right-left+1);
总结:
4.将x减到0的最⼩操作数(medium)
解析:
1)暴力:
2)优化:小demo
进窗口:ret+=nums[right]
出窗口:while(ret>target) ret-=nums[left++]
更新len: if(ret==target) len=max(len,right-left+1);
总结:
5.⽔果成篮(medium)
解析:
1)暴力:
2)优化:
进窗口:if (hash[fruits[right]]++ == 0) k++; 只要遇到 没遇见种类的水果时,k才++出窗口:while (left < n && k > 2) if (--hash[fruits[left++]] == 0) k--; 当这种水果数量变成0了,也就说明这种水果不存在了,种类k-1更新len:len = max(len, right - left + 1);
总结:
6.找到字符串中所有字⺟异位词(medium)
解析:
1).暴力:
2).小优化:
大优化:
进窗口:if(++hash2[s[right]]<=hash1[s[right]]) count++;
//分为两步,先是hash2[s[right]]++,然后判断是不是小于等于hash1[s[right]],是就说明,是一个有效字符
出窗口: if(right-left+1>n&&hash2[s[left]]--<=hash1[s[left++]]) count--;
判断减掉的这个 hash2[s[left]]--,是不是小于等于hash1[s[right]],是就说明,是一个有效字符
更新left: if(count==n) ret.push_back(left);
总结:
7.串联所有单词的⼦串(hard)
解析:
1).暴力
2).优化:
进窗口:string str = s.substr(right, len); //进窗口 hash2[str]++; if(hash1.count(str) && hash2[str] <= hash1[str]) count++; //记录有效字符串的个数
出窗口:if(right - left + len > m * len) { string _str = s.substr(left, len); if(hash1.count(_str) && hash2[_str] <= hash1[_str]) count--; hash2[_str]--; left += len; }
更新left:if(count == m) ret.push_back(left);
总结:
8.最⼩覆盖⼦串(hard)
解析:
1).暴力:
2).优化:
进窗口: if (hash1.count(s[right]) && ++hash2[s[right]] == hash1[s[right]]) count++;
出窗口: while (len>m&&count == hash1.size()) { if (len>m&&len > right - left + 1) { len = right - left + 1; _left = left; } if (hash1.count(s[left]) && --hash2[s[left]] < hash1[s[left++]]) count--; while (left < n && hash2[s[left]] == 0) left++;//跳过非种类字符 }
更新len:if(len>n) return ""; return s.substr(_left, len);
滑动窗口,引入:
顾名思义,就是在同向双指针的条件下创建的一个窗口大小,让两个指针left 和 right 从左向右移动。
滑动窗口,本质:就是同向双指针;
当两个指针都可以做到不回退,都能同向进行的时候,就可以采用滑动窗口
怎么用,定义left=0,right=0,来进窗口,right++;出窗口的时候更新left++;知道right==n时结束
对于滑动窗口的效率提升,是利用数组的单调性,就比如要枚举left-right之间数字相加之和==target,那么sum+=nums[right] 到某个值后>target了,就不用继续加后面的值了,这样就不用枚举后面所有的情况,避免了很多没有必要的枚举
时间复杂度:代码可能是两层嵌套循环,但是实际上就只是right指针和left指针从左走到右,不回头的操作,那么就是最大时间复杂度也只是n+n次=2n 是O(N)级别
1.⻓度最⼩的⼦数组(medium)
题目的意思就是找出最短子数组的和==target ,这是最经典的滑动窗口的入门问题了,可以现在就动手解决一下,如果没明白再看解析。
1.解析:给我们一个数组nums,要我们找出最小子数组的和==target,首先想到的就是暴力解法
1)暴力:
就是从第一个元素开始进行相加,遍历后面的每一个元素,直到right移动到n-1的位置,left++,right回头重新开始遍历相加,然后遇到和sum==target的时候就更新len,最后返回最短的len,但是这种做法想都不用想,时间复杂度是O(N^2) 绝对会超时
2)优化,滑动窗口:
再讲滑动窗口做法前,我们要考虑为什么可以采用滑动窗口,为什么选择滑动窗口。
任何解法都是在暴力下来做优化,有了暴力的解法,我们可以看出,只要固定left ,让right++,使sum和>= target 我们就可以停止right的移动,因为right继续后移,sum继续加和,是没有意义的。所以,我们这个时候停止righ的移动,那么right要会退吗?
答案是不用,因为sum已经记录了left 到 right 之间的和,我们没有必要让right回退后重新计算之间数字的和,而是 sum-=nums[left] 然后left++;这样就又可以保证left移动后,并且到right之间的值里面被算出来。
这样我们能够发现,left 和 right 两个指针并没有进行回退,而是一直向前走,我们称这种同向的双指针,为 “滑动窗口” 。
所以直到right==n时就结束移动。这样在不满足边界条件的前提下进行跟新len 和 让left++。
class Solution {
public:int minSubArrayLen(int target, vector<int>& nums) {int n=nums.size();int len=INT_MAX,sum=0;for(int left=0,right=0;right<n;right++){//进窗口sum+=nums[right];//出窗口while(sum>=target){//为什么要先更新len,因为如果进来的条件是sum==target 那么就是要先更新最小值,最后在left++len=min(len,right-left+1);sum-=nums[left++];}}return len==INT_MAX?0:len;}
};
通过上面,就已经可以分析出,“滑动窗口”系列就是:
1.进窗口
2.出窗口
3.更新值
2.⽆重复字符的最⻓⼦串(medium)
如果是按照leetcode顺序刷的话,有多少人都死在了这题上面,其实如果了解了“双指针”->"滑动窗口"的过度,能很简单解决这题!!!
解析:
1)仍然是暴力解法:
不含有重复字符,那么就考虑到从第一个元素开始遍历,存到一个字符串str里,如果遇到了重复字符,就回退right,让left++,在从头开始遍历,当然这样的O(N^2) 绝对会超时;
2)优化:
有暴力延伸出双指针,定义left 和 right 那么开始遍历的时候,right++,直到遇到重复的字符的时候,我们就让left++,直到跳过 跟s[right] 重复的同一个字符,
这时就可以写出,定义一个hash表来记录每个字符出现的次数,>2 就说明重复
进窗口:hash[s[right]]++;
出窗口: while(hash[s[right]]>1) hash[s[left++]]--;
更新len: right-left+1;
class Solution {
public:int lengthOfLongestSubstring(string s) {if(s.size()==1) return 1;unordered_map<char,int> hash;int n=s.size();int len=0;for(int left=0,right=0;right<n;right++){//进窗口hash[s[right]]++;//出窗口while(hash[s[right]]>1)hash[s[left++]]--;len=max(len,right-left+1);}return len;}
};
总结:滑动窗口题目模板都大差不差,这题因为是考虑去掉重复字符,那么可以在每次循环结束都来判断最大的len,也不用在while里判断,因为可能回造成s字符串没有重复的字符,那么就进不去while
3.最⼤连续1的个数III(medium)
只要不对数组直接进行调整,移动什么的,其实都是比较简单的~ 因为下标都是固定的
解析:
1)暴力:
我在这题也卡了很久,暴力无非就是从第一个元素开始,遇到1,right++,遇到0,k--,直到k=0,然后更新len=right - left + 1;然后left++,right回头 重新遍历,重新跟新left,那么绝对超时O(N^2);
2)优化:
通过暴力看也看出,每次right走到k==0 的时候都要回头重新遍历,那么这么多次重新遍历都是无效的,我们只需要用len记录后,left++,直到越过一个0,k+=1,说明right就又可以往后移动到一个为0 的位置,当遇到第二个0的时候right停下来,我们就再次更新len,就可以得到最长的len
这里要说明的是,我们可以定义一个_k ,来记录遇到0的个数,当_k==k 的时候,right仍然可以向后移动,这点一定要想清楚,只要当_k>0 ,那么边界条件被打破,这个时候就要left++,当nums[left]==0 的话,移动过去后,_k--;
仍然是:
进窗口:if(nums[right]==0) _k++;
出窗口:while(_k>k) if(nums[left++]==0) _k--;
更新len:len=max(len,right-left+1);
class Solution {
public:int longestOnes(vector<int>& nums, int k) {int len=0;for(int left=0,right=0,_k=0;right<nums.size();right++){if(nums[right]==0) _k++;while(_k>k) if(nums[left++]==0) _k--;len=max(len,right-left+1);}return len;}
};
总结:
滑动窗口,要从暴力推导到优化还是比较好想的,滑动窗口,是保证两个指针left跟right依次往前运动,不后退,保证了效率的提升,不用产生无用的操作。
这题就是1.先要保证进窗口,//进窗口,一定要在前面
if(nums[right]==0) _k++;
这个就是进窗口的条件,当nums[right]==0 _k++;
2.那么现在考虑出窗口,while(_k>k) 在这个循环条件里面进行出窗口,其实仔细一些,如果在_k==k 时出窗口是不完美的,你还要保证nums[right+1]这个时候也是等于0 的。所以条件过于复杂苛刻,不便于实现,那么在while里面进行出窗口实际上就是让left++ 只是多加了一个if在满足nums[left++]==0 的条件下,就_k-- 来实现出窗口,这样就又可以让right继续往后移动!!!
4.将x减到0的最⼩操作数(medium)
这里面藏着一个小demo ,如果想不上去,这题简直无脑到直逼困难题!!!没有人会一直写代码,列举所有左右的情况,然后去相减吧,题目的意思就有点牵着别人走了。
解析:
1)暴力:
暴力属实无脑,开始先减减左边,然后减减右边,可以用dfs+回溯来解决,但是压栈太多,题目数据大,肯定会爆,直到x被减为0,那么就返回被更新的次数,如果只是单纯的这么写,那简直不敢想代码量。
2)优化:小demo
我们可以看出,要用最小的操作来求出两边之和为x , 那么逆向过来,是不是就是用最大的操作次数 求出的和==sum-x;
那么翻译过来后就明白了,是在数组里面找出最大操作次数的和==sum - x ,那么我们就令
target == sum - x ,那么这题就跟第一题一样了。
仍然是:
进窗口:ret+=nums[right]
出窗口:while(ret>target) ret-=nums[left++]
更新len: if(ret==target) len=max(len,right-left+1);
class Solution {
public:int minOperations(vector<int>& nums, int x) {int sum=0;for(auto e : nums) sum+=e;sum-=x;if(sum<0) return -1;int len=-1,ret=0;for(int left=0,right=0;right<nums.size();right++){//进窗口ret+=nums[right];//出窗口while(ret>sum) ret-=nums[left++];//更新lenif(ret==sum) len=max(len,right-left+1);}return len==-1?-1:nums.size()-len;}
};
总结:
1.这题有很多关键点,如果只是按照题目要求来进行描写,x减一个左数,或减一个右数,实在是过于麻烦和复杂,时间复杂度绝对超时,那么就要逆向思维;
2.我们要求最短的操作数,那么就是在左右两个位置各找一堆数字的和正好是等于x的,加入所有数字和是sum,那么反过来的意思就是在这左右两堆数字中间的这堆数 之和正好是 sum - x 且要求他的长度是最长的,那么这个意思就立马变简单了,那么我们就可以采用滑动窗口,从最左边开始查找,一直找到最长串的数组和为sum - x 就能达到最好的目的,那么最后返回值就是nums.size()-len
3.细节问题:sum - x可能<0 那么就直接返回 -1
ret+=nums[right] 不用在乎ret < sum 的情况,因为while()里面一定会进行解决ret>sum 的问题 最后就是ret == sum 要单独拿出来进行更新len值
5.⽔果成篮(medium)
这题相对来说,还是比较简单的,有了上面几题的经验之后,这题只需要记录水果的种类个数不超过2,就可以一直装水果,然后记录最长子数组个数。
题目意思就是最多只能装两个不同种类的水果,求最大子数组的长度。
解析:
1)暴力:
那么就设置两个指针,left和right,从头开始遍历,设置一个计数器k,直到right遍历到第三个种类的水果时,停止,这个时候跟新len 的长度,在让left++,right回头重新进行遍历,那么绝对会超时,O(N^2);
2)优化:
就是设置left和right 从头开始遍历,当right遍历到第三个种类的时候,就不用回退,只需要left++直接跳过一个种类的水果的时候,这个right就可以进行往前移动,然后重复上面的操作,就不停的更新len,就可以得到最大长度的len
仍然是:
进窗口:if (hash[fruits[right]]++ == 0) k++; 只要遇到 没遇见种类的水果时,k才++
出窗口:while (left < n && k > 2) if (--hash[fruits[left++]] == 0) k--; 当这种水果数量变成0了,也就说明这种水果不存在了,种类k-1
更新len:len = max(len, right - left + 1);
class Solution {
public:int totalFruit(vector<int>& fruits) {int len = 0;int n = fruits.size(), k = 0;unordered_map<int,int> hash;for (int left = 0, right = 0, tmp = 0; right < n; right++){//进窗口if (hash[fruits[right]]++ == 0) k++;//出窗口while (left < n && k > 2)if (--hash[fruits[left++]] == 0) k--;//更新lenlen = max(len, right - left + 1);}return len;
}
};
总结:
滑动窗口,这一题就是在hash里面最多只能存放两种水果,
1.暴力,那么就是从第一个开始,依次加入水果,直到超过三个,就开始计算长度,left++ right在从头开始计算,想都不要想,这样的暴力绝对超时
2.优化:滑动窗口,因为考虑到,right走到第三种水果的时候,left就开始++,只需要减去最开始left 的这个水果,那么要设置一个计数器k,因为在最开始,我一直以为,hash减到0了hash.size() 就会变小,但其实不会,任然存在,并且也可以变成负数,所以要采用计数器k,if (hash[fruits[right]]++ == 0) k++;
存在新水果,k++;
if (--hash[fruits[left++]] == 0) k--;
超过水果个数k,那么就只要最开始left水果变成0,k就--,这样right就可以继续移动,
最后一直更新len的值就行
6.找到字符串中所有字⺟异位词(medium)
题目意思就是在s里找p,p的顺序是任意的,但是要包含所有字符,返回s里面所有满足条件的字符下标。没有思路的话,可以先试试暴力枚举:
解析:
1).暴力:
暴力枚举其实也有难度,比如要先讲p排序,不然要列举出p的各种顺序肯,太复杂,对比不过来,然后设置str+=s[right],长度len=right-left+1 == p.size(); 然后对str进行排序,在判断str==p? 如果等于就存入str的下标,否则就跳过,left和right都++;
2).小优化:
用两个hash表来比较纯如的字符是否完全相等,如果是,就加入left;但是这样会有一个用例超时,说明还可以继续优化。
class Solution {
public:vector<int> findAnagrams(string s, string p) {int n=s.size(),m=p.size();unordered_map<char,int> hash1;for(auto e : p) hash1[e]++;unordered_map<char,int> hash2;vector<int> v;for(int left=0,right=0;right<n;right++){hash2[s[right]]++;while(right-left+1>m) hash2[s[left++]]--;//判断两个字符数组是否相等if(hash1.count(s[right])){int f=0;for(int i=0;i<m;i++){if(hash2[p[i]]!=hash1[p[i]]){f=1;break;}}if(f==0) v.push_back(left);}}return v;}
};
大优化:
其实对于暴力解,要排序str,p 时间复杂度都是很高的代价,并且,每个字串str都要进行判断,真的太无效了,比如明知道str=“bae” p="abc",明知道二者不可能相等,还非要去比较,那这样显得太呆了,所有我们就要设置一个计数器count 来记录在这个固定的滑动窗口里面 有多少个有效的字符。
因为前面题目都是不固定长的窗口大小,这里是固定长的窗口大小,所以,我们出窗口的条件就跟前面题目不一样,这里只需要count==p.size() 就说明有效字符已经达到,加入left就ok,然后就可以进行出窗口。
所以还是比较简单的,复杂一点的位置和细节,就是要判断有效字符的个数count
进窗口:if(++hash2[s[right]]<=hash1[s[right]]) count++;
//分为两步,先是hash2[s[right]]++,然后判断是不是小于等于hash1[s[right]],是就说明,是一个有效字符
出窗口: if(right-left+1>n&&hash2[s[left]]--<=hash1[s[left++]]) count--;
判断减掉的这个 hash2[s[left]]--,是不是小于等于hash1[s[right]],是就说明,是一个有效字符
更新left: if(count==n) ret.push_back(left);
class Solution {
public:vector<int> findAnagrams(string s, string p) {int m=s.size(),n=p.size();unordered_map<char,int> hash1; for(auto e : p) hash1[e]++; unordered_map<char,int> hash2;int count=0;vector<int> ret;for(int left=0,right=0;right<m;right++){//进窗口if(++hash2[s[right]]<=hash1[s[right]]) count++;//出窗口if(right-left+1>n&&hash2[s[left]]--<=hash1[s[left++]]) count--;//更新leftif(count==n) ret.push_back(left);}return ret;}
};
总结:
1.这题很难,首先就是考虑暴力求解问题,对于字符串s里面找p的异位词,就是分别设置两个指针,left和right,从最左边开始找,每次right-left+1==p.size()的时候就判断这个字串是不是跟p的异位词相同,可以考虑把他门都存在两个hash数组中,进行判断,字符出现的次数是不是完全相同,这是暴力,每次当righe-left+1==p.size() 的时候,那么当right++,这个时候left就也要++,每次就重新判断right从left位置开始,重新把数组清空,在重复上面步骤进行比较。
2.就是对于right-left+1==p.size()的时候,进行比较,这个时候不用right回退,只需要left++,right++,也不用清空hash2表,只需要删除hash2[s[left++]]--,并且hash2[[right]]++,让left指针前进就行,相当于增加一个,删除一个字符,这就是典型的滑动窗口,left和right都不会回退,一直向前 ,并且满足进窗口,当right-left+1>m 的时候让left++,在出窗口即可。可是这样时间复杂度还是很高
3.那么就考虑用count计数器来记录有效字符的个数,每次只记录有效字符的个数,当 if(++hash2[s[right]]<=hash1[s[right]]) count++;
,减也是,只有减去的是有效字符,count才--
if(right-left+1>m) if(hash2[s[left]]--<=hash1[s[left++]]) count--;
并且只在right-left+1>m 超过这个范围的时候,才发生,那么每次只需要判断,有效字符个数达到p.size()的时候才就可以增加数据
7.串联所有单词的⼦串(hard)
这一题,简直就是跟上一题找异位词,百分之99都一样,所以一定要弄懂上一题。
题目意思任然是要在words中的每个单词都以任意顺序组合,然后再s里面找到这种组合的字串,返回所有满足条件的下标。
解析:
1).暴力
就是设置两个指针,left 和 right 然后往后判断,left 到 right 这个区间的字符串是不是能够等于words组合成的顺序,显然,这是绝对不可取的,要进行优化。
2).优化:
小demo,看到这种任意组合的字符串,我们不可能无序的组合,然后在s里面找字串去进行对比,这肯定不可取,那么我们就要跟上题大优化一样,设置计数器count 来记录有效字符串的个数,这样就可以很方便的进行记录。
可以看出,我们只需要讲words里面的字符串设置成a,b这种字符,因为words里面单词的长度都是相等的,那么设置int m=words[0].size() 来表示s里面假设的一个字符的长度,就比如string str=s.substr(left,m);就将str比作是一个字符。那么每次right+=m.left+=m 实际上每次都是跳过m个字符,然后也是设计一个count来计数,当这个str是有效字符的时候count++,设置_str,满足有效字符的时候就left+=m,出窗口。具体更上一题一模一样。
进窗口:string str = s.substr(right, len);
//进窗口
hash2[str]++;
if(hash1.count(str) && hash2[str] <= hash1[str]) count++; //记录有效字符串的个数
出窗口:if(right - left + len > m * len)
{
string _str = s.substr(left, len);
if(hash1.count(_str) && hash2[_str] <= hash1[_str]) count--;
hash2[_str]--;
left += len;
}
更新left:if(count == m) ret.push_back(left);
class Solution {
public:vector<int> findSubstring(string s, vector<string>& words) {int n = s.size(), m = words.size(), len = words[0].size();unordered_map<string, int> hash1; for(auto& e : words) hash1[e]++;vector<int> ret;for(int i = 0; i < len; i++){unordered_map<string, int> hash2;for(int left = i, right = i, count = 0; right + len <= n; right += len){string str = s.substr(right, len);//进窗口hash2[str]++;if(hash1.count(str) && hash2[str] <= hash1[str]) count++;//出窗口 固定的滑动窗口if(right - left + len > m * len) {string _str = s.substr(left, len);if(hash1.count(_str) && hash2[_str] <= hash1[_str]) count--;hash2[_str]--;left += len;}if(count == m) ret.push_back(left);}}return ret;}
};
总结:
跟上一题固定滑动窗口大小简直一模一样,不一样的就是上一题是滑动字符大小,这次是滑动字符串大小;
1.任然是设置两个hash表进行存入字符串,用count来记录有效字符串的个数
2.区别就是在进窗口的时候要设置str=s.substr(right,m);
在出窗口的时候设置_str=s.substr(left,m);3.优化,就是要对hash1里面是否存在str 和 _str 进行判断,这样可以减少查询时间
8.最⼩覆盖⼦串(hard)
经过上面那么多题的磨练,这题是我为数不多的自信题,调试了两个小时第一次自己解决一道困难题,其实相对来说还是比较简单的。
题目意思就是找到最小字串,要涵盖t中的全部字符,可以重复包含。
解析:
1).暴力:
任何优化都是通过暴力推导的,所以暴力真的很重要。
暴力就是无脑遍历s,找到字符串长度要大于等于t.size() 然后进行比较,如果包含全部t的字符,就记录长度len,直到求出最短长度。肯定会超时,而且暴力不好实现,所以还是要优化。
2).优化:
那么还是双指针问题,只要right++,判断有一个有效字符,那么count++,直到包含所有的字符,就开始更新len,但是!!!有很多细节问题,比如:
1).在len 比 s.size() 还要长,明显就是没有进入该条件判断,只能返回空串
2).在执行一次后 可能存在 s="bba" t="ab" 这种情况,那么必须要保证 left继续++,所以这个判断应该在while里面判断才行,这样count 的种类数没有减少,说明字串任然可以减少,
3.存在s="aa" t="aa" 说明 可能会加入一个a count就满足种类达标了,那么并不能就这样返回值,所以还要判断只有在len>t.size()的时候才可以采取,否则就未达标。
进窗口:
if (hash1.count(s[right]) && ++hash2[s[right]] == hash1[s[right]]) count++;
出窗口: while (len>m&&count == hash1.size())
{
if (len>m&&len > right - left + 1)
{
len = right - left + 1;
_left = left;
}
if (hash1.count(s[left]) && --hash2[s[left]] < hash1[s[left++]]) count--;
while (left < n && hash2[s[left]] == 0) left++;//跳过非种类字符
}
更新len:if(len>n) return "";
return s.substr(_left, len);
class Solution {
public:string minWindow(string s, string t) {int n = s.size(), m = t.size();if (n < m) return "";unordered_map<char, int> hash1; for (auto e : t) hash1[e]++;unordered_map<char, int> hash2;int _left = 0, len = INT_MAX;int left = 0, right = 0;while (right < n && hash1.count(s[right]) == 0) right++, left++;if (left >= n || right >= n) return "";int count = 0;//记录种类for (right; right < n; right++){//进窗口if (hash1.count(s[right]) && ++hash2[s[right]] == hash1[s[right]]) count++;//出窗口while (len>m&&count == hash1.size()){if (len>m&&len > right - left + 1){len = right - left + 1;_left = left;}if (hash1.count(s[left]) && --hash2[s[left]] < hash1[s[left++]]) count--;while (left < n && hash2[s[left]] == 0) left++;//跳过非种类字符}}if(len>n) return "";return s.substr(_left, len);
}
};
因子都是成对出现的,那么我们就只需要在for里面判断 x*x < n 那么就判断是否有一个m%x==0 那么当x>k 就满足,另一个因子就是 m / x > k 就也会满足。
总结一下,“滑动窗口”问题其实也就是模板问题,只要能明白“该问题是同向的双指针问题”,那么就可以往滑动窗口靠,就是“查找子数组”,就分三大点:进窗口_出窗口_更新len!!!
我觉得我自己的进步挺大的,希望对你也有帮助哦!~
相关文章:

专题二_滑动窗口_算法专题详细总结
目录 滑动窗口,引入: 滑动窗口,本质:就是同向双指针; 1.⻓度最⼩的⼦数组(medium) 1.解析:给我们一个数组nums,要我们找出最小子数组的和target,首先想到的…...

【机器学习-三-无监督学习】
无监督学习 什么是无监督学习分类聚类降维 有监督和无监督学习的区别 上一节介绍了监督学习,下面来介绍无监督学习,这也是最广泛应用的算法。 什么是无监督学习 上一节中,我们知道了监督学习是通过 对算法,**输入一对数据&#x…...

JAVA基础:Lambda表达式(上)
前言 Lambda表达式是jdk1.8的一个新特性,他属于一种语法堂主要作用是对匿名内部类语法简化 lambda基本应用 lambda表达式想要优化匿名内部类是有前提条件,首先必须是一个接口,而且要求接口中只能有1个抽象方法,称之为函数式接口…...

Vue使用fetch获取本地数据
(1)使用get test.json文件 { "list":[111,222,333] } <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initi…...

《酒饮真经》秘籍4,让你的酒场技巧更上一层楼!
在酒桌这一独特的舞台上,每个人都扮演着不同的角色,或攻或守,尽显智慧与风度。对于不擅长喝酒的人来说,如何在推杯换盏间既保护自己又不失礼节,是值得我们仔细研究的。下面是酱酒亮哥为您整理的一系列实用的酒桌攻防秘…...

回车符与快捷键记录
一.在Windows和Linux操作系统中,回车符(或称为换行符)的处理方式区别 1.Windows下的回车符 在Windows系统中,回车符通常是由两个字符组成的序列:回车符(Carriage Return,简称CR,AS…...

计算机网络-VRRP工作原理
一、VRRP工作原理 前面我们大概了解了VRRP的一些基础概念,现在开始学习VRRP的技术原理。VRRP的选举及工作步骤: 确定网关地址 选举主备 主设备发送VRRP报文通知Backup设备 主设备响应终端ARP并维持在Master状态 终端正常发送报文到网关进行转发 因为我们…...

6.5椒盐噪声
在OpenCV中联合C给一张图片加上椒盐噪声(Salt and Pepper Noise)可以通过随机选择像素点并将其置为黑色(0)或白色(255)来实现。椒盐噪声是一种随机噪声,通常表现为图像中的孤立黑点(…...

CSS样式的引用方式以及选择器使用
1. CSS 引用方式 CSS 可以通过三种方式引用到 HTML 文件中: 行内样式(Inline Styles):直接在 HTML 元素中定义样式。内部样式表(Internal CSS):在 HTML 文档的 <head> 部分使用 <sty…...

Python Flask_APScheduler定时任务的正确(最佳)使用
描述 APScheduler基于Quartz的一个Python定时任务框架,实现了Quartz的所有功能。最近使用Flask框架使用Flask_APScheduler来做定时任务,在使用过程当中也遇到很多问题,例如在定时任务调用的方法中需要用到flask的app.app_context()时&#…...

Linux命名管道
通信的前提是让不同的进程看到同一份资源,因为路径是具有唯一性的,所以我们可以使用路径文件名来唯一的让不同进程看到同一份资源,实现没有血缘关系的两个进程进行管道通信 1.指令级 mkfifio(FILENAME,0666) …...

Xinstall助力App全渠道统计,参数传递下载提升用户体验!
在移动互联网时代,App已成为我们日常生活中不可或缺的一部分。然而,对于App开发者来说,如何有效地推广和运营自己的应用,却是一个不小的挑战。尤其是在面对众多渠道、复杂的数据统计和用户需求多样化的情况下,如何精准…...

【时时三省】(C语言基础)指针进阶 例题4
山不在高,有仙则名。水不在深,有龙则灵。 ----CSDN 时时三省 strlen是求字符串长度 这个需要算上\0 第一个arr 是打印6 因为它加上\0是有六个元素 第二个arr0 数组名相当于首元素的地址 a的地址加0还是a的地址 所以这个地方还是…...

k8s的配置管理
一、配置管理分为两种: 1. 加密配置:用来保存密码和token密钥对以及其它敏感的k8s资源。 2.应用配置:我们需要定制化的给应用进行配置,我们需要把定制好的配置文件同步到pod当中的容器。 二、加密配置 1.secret三种类型…...

JAVA- 多线程
一,多线程的概念 1.并行与并发 并行:多个任务在同一时刻在cpu 上同时执行并发:多个任务在同一时刻在cpu 上交替执行 2.进程与线程 进程:就是操作系统中正在运行的一个应用程序。所以进程也就是“正在进行的程序”。࿰…...

【Qt】解决设置QPlainTextEdit控件的Tab为4个空格
前言 PyQt5 是一个用于创建跨平台桌面应用程序的 Python 绑定集合,它提供了对 Qt 应用程序框架的访问。用于开发具有图形用户界面(GUI)的应用程序,以及非GUI程序。PyQt5 使得 Python 开发者可以使用 Qt 的丰富功能来构建应用程序。…...

elementUI根据列表id进行列合并@莫成尘
本文章提供了elementUI根据列表id进行列合并的demo,效果如图(可直接复制代码粘贴) <template><div id"app"><el-table border :data"tableList" style"width: 100%" :span-method"objectS…...

基于人工智能的智能安防监控系统
目录 引言项目背景环境准备 硬件要求软件安装与配置系统设计 系统架构关键技术代码示例 数据采集与预处理模型训练与预测实时监控与检测应用场景结论 1. 引言 随着科技的发展,智能安防监控系统逐渐成为家庭、企业和公共场所保障安全的核心工具。通过人工智能和计…...

分享从零开始学习网络设备配置--任务6.3 使用基本ACL限制网络访问
任务描述 某公司构建了互联互通的办公网,为保护公司内网用户数据的安全,该公司实施内网安全防范措施。公司分为经理部、财务部和销售部,分属3个不同的网段,3个部门之间用路由器进行信息传递。为了安全起见,公司领导要求…...

数据结构——线性表(静态链表、循环链表以及双向链表)
1、静态链表 用数组描述的链表叫做静态链表,这种描述方法叫做游标实现法。 静态链表需要对数组的第一个和最后一个元素作为特殊元素处理,不存数据。 最后一个指向第一个有数据的下标地址,第一个游标指向第一个没有数据的下标地址。 我们对…...

vue3_对接腾讯_实时音视频
项目需要对接腾讯的实时音视频产品,我这里选择的是多人会议,选择其他实时音视频产品对接流程也一样,如何对接腾讯实时音视频的多人会议产品,从开通服务到对接完成,一 一讲解。 一、开通腾讯实时音视频 1.腾讯实时音视…...

一台电脑对应一个IP地址吗?探讨两台电脑共用IP的可能性
在当今数字化时代,IP地址作为网络世界中的“门牌号”,扮演着至关重要的角色。它负责在网络上唯一标识每一台设备,使得数据能够在庞大的互联网中准确无误地传输。然而,对于IP地址与电脑之间的对应关系,许…...

XInput手柄输入封装
功能全面地封装了XInput的输入, 1. 普通按钮按下, 按住, 弹起状态检查, 2. 摇杆4个方向的按下, 按住, 弹起检查 3. 按键状态变化检测并且记录按下触发时间, 按住保持时间, 方便用来完全自定义的输入功能 4. 多手柄输入合并 CXinputHelper.h #pragma once #include <win…...

NodeMCU-ESP8266+flash_download_tool_3.9.7 烧录
USB-TTL 接 NodeMCU的RXD0, TXD0, GND 例程hello_world: Eclipse编译信息: python /d/ESP/ESP8266_RTOS_SDK/ESP8266_RTOS_SDK/components/esptool_py/esptool/esptool.py --chip esp8266 --port COM6 --baud 115200 --before default_reset --after …...

首例开源的自动驾驶混合运动规划框架,手握“规划可解释”和“决策准确”两张王牌!
导读: 本文开发了一种新的混合运动规划方法,将环境和预测信息集成在Frenet坐标系中,提升了运动规划能力。本文将传统运动规划算法的可预测性和稳定性与RL的动态适应性相结合,从而形成了一个能够有效管理复杂情况并适应不断变化的环…...

数据结构之红黑树的 “奥秘“
目录: 一.红黑树概念 二. 红黑树的性质 三.红黑树的实现 四.红黑树验证 五.AVL树和红黑树的比较 一.红黑树概念 1.红黑树,是一种二叉搜索树,但在每个结点上增加一个存储位表示结点的颜色,可以是Red或Black。 通过对任何 一条从根…...

【鸿蒙 HarmonyOS NEXT】使用EventHub进行数据通信
✨本人自己开发的开源项目:土拨鼠充电系统 ✨踩坑不易,还希望各位大佬支持一下,在GitHub给我点个 Start ⭐⭐👍👍 ✍GitHub开源项目地址👉:https://github.com/cheinlu/groundhog-charging-syst…...

大模型RAG实战|构建知识库:文档和网页的加载、转换、索引与存储
我们要开发一个生产级的系统,还需要对LlamaIndex的各个组件和技术进行深度的理解、运用和调优。本系列将会聚焦在如何让系统实用上,包括:知识库的管理,检索和查询效果的提升,使用本地化部署的模型等主题。我将会讲解相…...

江协科技stm32————11-5 硬件SPI读写W25Q64
一、开启时钟,开启SPI和GPIO的时钟 二、初始化GPIO口,其中SCK和MOSI是由硬件外设控制的输出信号,配置为复用推挽输出 MISO是硬件外设的输入信号,配置为上拉输入,SS是软件控制的输出信号,配置为通用推挽输出…...

网络编程day04(UDP、Linux IO 模型)
目录 【1】UDP 1》通信流程 2》函数接口 1> recvfrom 2> sendto 3》代码展示 1> 服务器代码 2> 客户端代码 【2】Linux IO 模型 场景假设一 1》阻塞式IO:最常见、效率低、不耗费CPU 2》 非阻塞 IO:轮询、耗费CPU,可以处…...