滑动窗口小结

1.滑动窗口的思想

所谓滑动窗口,就是不断的调节子序列的起始位置和终止位置,从而得出我们想要的结果。这是数组操作中的一个重要的方法。

2. 滑动窗口相关例题

2.1 长度最小的子数组

这道例题的核心就在于通过双指针不断调节窗口的起始位置

长度最小的子数组

class Solution {
    //移动窗口:1.两个指针
    //2.核心思路:在于调整滑动窗口起始的位置
    public int minSubArrayLen(int target, int[] nums) {
        int sum =0;//子数组的和
        
        int left =0;//定义滑动窗口起始位置
        int result = Integer.MAX_VALUE;
        for(int right =0;right<nums.length;right++){
            sum+=nums[right];//记录子数组的和
            
            while(sum>=target){
                result=Math.min(result,right-left+1);//取最小值
                sum-=nums[left++];//从左向右减小
            }
        }
        return result==Integer.MAX_VALUE?0:result;
    }
}

2.2 水果成篮

本题的重点在于创建一个新数组,用来嵌套原数组,用这种方法来进行数组操作。这也算是一个小技巧吧。
思路:
1.当没有采摘过当前水果时,count记录,更新水果的尾部.(每次添加一种新水果都要更新尾部)
2.当采摘了两种水果时,更新此时的最大数量。
3.当超过两种水果时,清空第一种水果,并且将首部右移。
4.一定要注意更新水果的尾部(容易忽略)…

水果成篮

class Solution {
    /*
    *  测试用例(直观一点较好):1,2,3,2,3,1
    */
    public int totalFruit(int[] fruits) {
        int left = 0;
        int count = 0;//记录共有几种水果
        int len = fruits.length;
        int [] nums =new int[len];//新数组,用来存放水果
        int ans = 0;//可以收集水果的最大数量
        int renewLeft = 0;//用来更新水果的尾端

        for(int right=0;right<len;right++){
            //第一次采摘该种类的水果
            if(nums[fruits[right]]==0){
                count++;
                renewLeft=right; 
            }
            nums[fruits[right]]+=1;//更新该种水果的数量

            //更新可以收集水果的最大数目
            if(count==2){
                ans= ans>(nums[fruits[left]]+nums[fruits[renewLeft]])?
                ans:    nums[fruits[left]]+nums[fruits[renewLeft]];
            }
            // 当超过两种水果时,清空第一种水果
            while(count>2){
                nums[fruits[left]]--;//减少第一种水果的数量
                if(nums[fruits[left]]==0) count--;//直到第一种水果清空,最大数目减一
                left++;
            }
        }
        if(count==1) return nums[fruits[left]]; //如果只有一种水果
        return ans;
    }
}

2.3 最小覆盖子串

思路:
1.创建一个新数组用来进行操作数据,与上一个例题同
2.首先遍历字符串t,得到我们所需要的字符的种类与个数
3.遍历字符串s,筛选出我们所需的字符(即该元素数量大于0),并且每筛选出一个就将该元素的数量减一。直至我们筛选出所有的所需元素。那么,这个时候新的问题又产生了,我们无法保证该子序列只包含我们所需的元素。
4.因此我们需要筛选出多余的元素(该元素数量小于0,说明1.我们需要该元素,但是该元素的数量过多,2.我们不需要这种元素。)。因此,我们将串口缩小,同时每找到一个新元素,就将该元素数量加一,用来作为循环是否结束的标准。并且将左窗口不断右移动。
5.确定好新的窗口 ,在每个新的窗口中找最小的窗口,即输出长度
6.开始下一个窗口的寻找 这时候left指向的s字串 是t的第一个字符。我们要看剩下的字串中有没有满足的, 首先让当前指向的字符不算,然后指针右移。
7.至此为止,便是我们一个循环结束。。。。。

最小覆盖子串

class Solution {
     public String minWindow(String s, String t) {
        if (s == null || t.length() == 0  ) {
            return "";
        }
        // 将需要的字符个数记录下(t)
        int[] window = new int[128];
        for (int i = 0; i < t.length(); i++) {
            window[t.charAt(i)] += 1;
        }

        int right = 0, left = 0, count = t.length(), size = Integer.MAX_VALUE, start = 0;
        // 扩大窗口,先找到满足含有t s的字串
        while (right < s.length()) {
            // window里>=0都是需要的count,<0的都是非必要字符
            char c = s.charAt(right);
            // 判断窗口是否需要当前元素,需要的话count-1
            if (window[c] > 0) {
                count--;
            }
            window[c] -= 1; // 更新想要得到的窗口
            // 当count=0 说明需要的字符都被找到了,左指针右移 缩小窗口,直到刚好满足count==0
            if (count == 0) {
                while (window[s.charAt(left)] < 0) {
                    window[s.charAt(left)]++;
                    left++;
                }
                // 确定好新的窗口 在每个新的窗口中 找最小的窗口
                if (right - left < size) {
                    // size指向下标为确定好窗口的最后一个字符
                    size = right - left;
                    start = left;
                }
                // 开始下一个窗口的寻找 这时候left指向的s字串 是t的第一个字符
                // 我们要看剩下的字串中有没有满足的, 首先让当前指向的字符不算,然后指针右移
                window[s.charAt(left)]++;
                left++;
                count++;
            }
            right++; // 右指针不断右移,窗口不断扩大
        }

        return size == Integer.MAX_VALUE ? "" : s.substring(start, start + size + 1);
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值