【leetcode个人练习记录】 滑动窗口的问题

本文详细介绍了滑动窗口算法的核心思想和应用场景,通过LeetCode的两道题目1004.最大连续1的个数和1052.爱生气的书店老板进行实战解析,阐述了如何利用滑动窗口解决最大区间问题和连续子数组求和问题。重点在于找到不满足条件的情况来调整窗口,以及理解左右指针的动态移动策略。
摘要由CSDN通过智能技术生成

1.从本质上来看 ,滑窗是双指针,一根指针指向左端点,一根指针指向右端点。
2.右指针移动是可以表示扩张窗口,左指针移动表示缩小窗口。
3.如果当前元素满足需求时,可以挪动右指针尝试更优解,并且更新需要记录的变量(元素,元素个数++等)
4. 如果当前窗口内的元素不满足条件,可以挪动左指针尝试调整,并且更新需要记录的变量(元素,元素个数–等)
5. 通过以上步骤窗口就可以滑动起来了,在滑动的过程中及时更新答案,一般这类问题都是求最大或者最小

关键点在于找到一个 不满足条件的情况 使得左指针移动,这样可以简化问题。找到不合理的情况并且及时调整是滑动窗口的关键。

因为滑动窗口的两个指针移动的过程和虫子爬动的过程非常像:前脚不动,把后脚移动过来;后脚不动,把前脚向前移动。

Leetcode 滑动窗口类型题 <持续更新>

1004.最大连续1的个数|||

在这里插入图片描述

class Solution {
    public int longestOnes(int[] A, int K) {
        int N=A.length;
        int left=0;
        int right=0;
        int res=0;
        int zero_number=0;
        while(right<N){
            if(A[right]==0)
                zero_number++;
            while(zero_number>K){
                if(A[left++]==0)
                    zero_number--;
            }
            res=Math.max(res,right-left+1);
            right++;
        }
        return res;
    }
}

滑动窗口模板:

def findSubArray(nums):
    N = len(nums) # 数组/字符串长度
    left, right = 0, 0 # 双指针,表示当前遍历的区间[left, right],闭区间
    sums = 0 # 用于统计 子数组/子区间 是否有效,根据题目可能会改成求和/计数
    res = 0 # 保存最大的满足题目要求的 子数组/子串 长度
    while right < N: # 当右边的指针没有搜索到 数组/字符串 的结尾
        sums += nums[right] # 增加当前右边指针的数字/字符的求和/计数
        while 区间[left, right]不符合题意:# 此时需要一直移动左指针,直至找到一个符合题意的区间
            sums -= nums[left] # 移动左指针前需要从counter中减少left位置字符的求和/计数
            left += 1 # 真正的移动左指针,注意不能跟上面一行代码写反
        # 到 while 结束时,我们找到了一个符合题意要求的 子数组/子串
        res = max(res, right - left + 1) # 需要更新结果
        right += 1 # 移动右指针,去探索新的区间
    return res

滑动窗口中用到了左右两个指针,它们移动的思路是:以右指针作为驱动,拖着左指针向前走。右指针每次只移动一步,而左指针在内部 while 循环中每次可能移动多步。右指针是主动前移,探索未知的新区域;左指针是被迫移动,负责寻找满足题意的区间。

模板的整体思想是:

定义两个指针 left 和 right 分别指向区间的开头和结尾,注意是闭区间;定义 sums 用来统计该区间内的各个字符出现次数;
第一重 while 循环是为了判断 right 指针的位置是否超出了数组边界;当 right 每次到了新位置,需要增加 right 指针的求和/计数;
第二重 while 循环是让 left 指针向右移动到 [left, right] 区间符合题意的位置;当 left 每次移动到了新位置,需要减少 left 指针的求和/计数;
在第二重 while 循环之后,成功找到了一个符合题意的 [left, right] 区间,题目要求最大的区间长度,因此更新 res 为 max(res, 当前区间的长度) 。
right 指针每次向右移动一步,开始探索新的区间。

模板中的 sums 需要根据题目意思具体去修改,本题是求和题目因此把sums 定义成整数用于求和;如果是计数题目,就需要改成字典用于计数。当左右指针发生变化的时候,都需要更新 sums 。
另外一个需要根据题目去修改的是内层 while 循环的判断条件,即: 区间 [left, right][left,right] 不符合题意 。对于本题而言,就是该区间内的 0 的个数 超过了 2

补充一个不需要缩小窗口的代码,窗口扩大的条件是:窗口元素之和 + K >= 窗口的尺寸

class Solution {
    public int longestOnes(int[] A, int K) {
        int left=0;
        int right=0;
        int all_sum=0;
        int N=A.length;
        while (right < N) {
            all_sum+=A[right];
            if(all_sum+K<right-left+1){
                all_sum-=A[left];
                ++left;
            }
            ++right;
        }
        return right-left;
    }
}

1052. 爱生气的书店老板

在这里插入图片描述

解析:
由于「技巧」只会将情绪将「生气」变为「不生气」,不生气仍然是不生气。
我们可以先将原本就满意的客户加入答案,同时将对应的 customers[i] 变为 0。
之后的问题转化为:在 customers 中找到连续一段长度为 x 的子数组,使得其总和最大。这部分就是我们应用技巧所得到的客户。

class Solution {
    public int maxSatisfied(int[] cs, int[] grumpy, int x) {
        int n = cs.length;
        int ans = 0;
        for (int i = 0; i < n; i++) {
            if (grumpy[i] == 0) {
                ans += cs[i];
                cs[i] = 0;
            }
        }
        int max = 0, sum = 0;
      
        for (int right = 0, left = 0; right < n; right++) {
            sum += cs[right];
            if (right - left + 1 > x) {
                     sum-=cs[left];
                     ++left;
            }
            max=Math.max(max,sum);
        }
        return ans + max;
    }
}
class Solution {
    public int[][] flipAndInvertImage(int[][] A) {
        int n=A.length;
        for(int i=0;i<n;i++){
            for(int j=0;j<(n+1)/2;j++){
                int tmp=A[i][j]^1;
                A[i][j]=A[i][n-j-1]^1;
                A[i][n-j-1]=tmp;
            }
        }
        return A;
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值