滑动窗口各类题目(正在持续更新!!!) <-点击即可跳转
前言
我会将一些常用的算法以及对应的题单给写完,形成一套完整的算法体系,以及大量的各个难度的题目,目前算法也写了几篇,滑动窗口的题单正在更新,其他的也会陆陆续续的更新,希望大家点赞收藏我会尽快更新的!!!
滑动窗口
1.什么是滑动窗口
顾名思义滑动窗口就像是窗口一样,利用两个指针(窗扇)移动来遍历子区间(窗扇覆盖的面积)。
注意:[1,5]有五个子区间,[1,5],…[4,5],[5,5]。
2.滑动窗口使用条件
区间权值大小满足随区间长度单调变化,即区间越长区间权值越小(或越大),即当求最大值(最小值)集合内全是正数(负数),后面会讲原因 。滑动窗口是用来求有一定限制的区间个数或最短的区间等等。
3.滑动窗口使用优势
相比于双重for循环,滑动窗口有两个优势:
1.当条件不满足时右指针就向右移动一格。
2.左指针从不回退,即只往右走。
那么为什么不满足条件时右指针就向右移动一格?左指针从不回退,即只往右走?这就是区间越长区间权值越小(或越大)的原因。如图 (找出大于等于7的最小区间):
如图所示,因为3+4 = 7,所以3 + 3 + 4 > 7所以l不需要回退。
当我们将4换成-3后由于不满足条件时右指针就向右移动一格,错过了7这个条件,所以这就是必须满足区间越长区间权值越小(或越大)的原因。
所以用两句话概括就是:
当条件不满足时右指针就向右移动一格:不会去枚举到一定不满足条件的区间。
左指针从不回退,即只往右走:不会枚举符合条件,但是一定不会是答案的区间。
4.滑动窗口的解法
1.右指针向右移动先找到第一个满足目标的窗口,然后再判断左右指针那个移动。如:
//先找到从第一个开始满足目标的窗口
while (r < numsSize && sum < target) {
sum += nums[r++];
}
//移动两个指针
while (r <= numsSize) {
if (sum >= target) {
sum -= nums[l++];
ans = ans < (r - l + 1)? ans : (r - l + 1);
} else {
if (r < numsSize) {
sum += nums[r++];
} else {//这个else用来防止r越界的,如果r来到最后一个位置因为r++就直接越界了,当前面l++完了后就直接break
break;
}
}
}
2.直接判断左右指针哪个移动:
for (int i = 0, j = 0;j < numsSize;j++){
sum += nums[j];
while (sum >= target){
int x= j - i + 1;
result = result < x ? result : x;
sum -= nums[i];
i++;
}
}
如果大家看不懂这段话,可以结合下面的题目来理解。
5.滑动窗口题目(定窗口滑动和变化窗口)
5.1变化窗口
int minSubArrayLen(int target, int* nums, int numsSize) {
int result = INT_MAX;
int sum = 0;
//看似两个循环时间复杂度很高,其实用了滑动窗口时间复杂度很低为O(n)
//其中j指针每次for循环都在移动,而i指针只有在特定情况移动,所以也可以叫i,j为快慢指针。
for (int i = 0, j = 0;j < numsSize;j++){
sum += nums[j];
while (sum >= target){
int x= j - i + 1;
result = result < x ? result : x;
sum -= nums[i];
i++;
}
}
result = result == INT_MAX ? 0 : result;//有可能有0这个边界
return result;
}
int minSubArrayLen(int target, int* nums, int numsSize){
//先判断是否为空
if (numsSize == 0) {
return 0;
}
int ans = INT_MAX;
int l = 0;
int r = 0;
int sum = 0;
//先找到从第一个开始满足目标的窗口
while (r < numsSize && sum < target) {
sum += nums[r++];
}
//移动两个指针
while (r <= numsSize) {
if (sum >= target) {
sum -= nums[l++];
ans = ans < (r - l + 1)? ans : (r - l + 1);
} else {
if (r < numsSize) {
sum += nums[r++];
} else {//这个else用来防止r越界的,如果r来到最后一个位置因为r++就直接越界了,当前面l++完了后就直接break
break;
}
}
}
return ans == INT_MAX? 0 : ans;
}
5.2定窗口滑动
int minimumRecolors(char* blocks, int k) {
//这道题可以看成在k这个区间最少有几个白色
int len = strlen (blocks);
int ans = INT_MAX;
int cnt = 0;
for (int i = 0, j = 0; i < len; i++) {
if (blocks[i] == 'W') {
cnt++;
}
while (i - j + 1 == k) {
ans = ans < cnt ? ans : cnt;
if (blocks[j] == 'W') {
cnt--;
}
j++;
}
}
return ans;
}
int minimumRecolors(char* blocks, int k) {
int ans = 0;
int n = strlen(blocks);
for (int i = 0;i < k;i++){
if(blocks[i] == 'W')ans++;
}
int cnt = ans;
for(int i = k;i < n;i++){
if(blocks[i - k] == 'W')ans--;
if(blocks[i] == 'W')ans++;
cnt = ans < cnt ? ans :cnt;
}
return cnt;
}
5.3变化窗口
int numSubarrayProductLessThanK(int* nums, int numsSize, int k) {
int cnt = 0;
int sum = 1;
for (int i = 0, j = 0; i < numsSize; i++) {
sum *= nums[i];
while (sum >= k && j <= i) {
sum /= nums[j];
j++;
}//这里大家可能会加个 if(sum < k)再加这个cnt,其实是不要的,即使k = 0,因为在判断的时候j<=i,使i-j+1 = 0,所以cnt = 0
cnt += i - j + 1;
}
return cnt;
}
总结
滑动窗口还有很多类型,一次写不完,大家可以点击上面的题单跳转到题单中,其他的题单也会慢慢更新,链接也会贴在各个算法的讲解中,大家可以收藏方便以后快速找到,所以希望大家收藏,以后想写题目就可以直接点击相应的算法文章。