【算法学习之路】2.滑动窗口


滑动窗口各类题目(正在持续更新!!!) <-点击即可跳转

前言

我会将一些常用的算法以及对应的题单给写完,形成一套完整的算法体系,以及大量的各个难度的题目,目前算法也写了几篇,滑动窗口的题单正在更新,其他的也会陆陆续续的更新,希望大家点赞收藏我会尽快更新的!!!

滑动窗口

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变化窗口

力扣 209. 长度最小的子数组

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定窗口滑动

力扣2379. 得到 K 个黑块的最少涂色次数

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变化窗口

力扣713. 乘积小于 K 的子数组

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;
}

总结

滑动窗口还有很多类型,一次写不完,大家可以点击上面的题单跳转到题单中,其他的题单也会慢慢更新,链接也会贴在各个算法的讲解中,大家可以收藏方便以后快速找到,所以希望大家收藏,以后想写题目就可以直接点击相应的算法文章。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

零零时

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值