【Leetcode数组--子数组--滑动窗口】209. 长度最小的子数组 904. 水果成篮 1004. 最大连续1的个数 III 76. 最小覆盖子串(有数组操作中重要的方法:滑动窗口!!!!)

Leetcode209

1.问题描述

在这里插入图片描述

2.解决方案

解法一:两个错误思路的算法

错误的,要求连续子数组,这都不连续了!

//错误的,要求连续子数组,这都不连续了
class Solution {
public:
    int minSubArrayLen(int target, vector<int>& nums) {
        sort(nums.begin(),nums.end());
        int sum=0;
        int cnt=0;
        bool flag= false;
        for(int i=nums.size()-1;i>=0;i--){
            sum+=nums[i];
            cnt++;
            if(sum>=target){
                flag=true;
                break;
            }
        }
        if(flag==true) return cnt;
        else return 0;
    }
};

思路就是找到最大值之后,从最大值开始往左右遍历,但是想一想只是大部分情况是这样的而这个子数组不一定要包含这个最大值,而且这只能找到一个以最大值为边界的子数组,那就更不一定对了

//错误的,谁说一定是从最大值开始找呢,那可不一定
class Solution1 {
public:
    int minSubArrayLen(int target, vector<int>& nums) {
        //找到最大值对应下标
        int index=-1;
        int max=INT32_MIN;
        for(int i=0;i<nums.size();i++){
            if(nums[i]>max){
                max=nums[i];
                index=i;
            }
        }

        //
        int left=index-1;
        int right=index+1;
        int sumLeft=max;
        int sumRight=max;
        int cntLeft=1;
        int cntRight=1;
        if(max>=target) return 1;
        while(1){
            if(left<0&&right>nums.size()-1) break;
            if(left>=0){
                sumLeft+=nums[left];
                left--;
                cntLeft++;
                if(sumLeft>=target) return cntLeft;
            }
            if(right<=nums.size()-1){
                sumRight+=nums[right];
                right++;
                cntRight++;
                if(sumRight>=target) return cntRight;
            }
        }
        if(left<0&&right>nums.size()-1) return 0;

        //百分之百不会从这走
        return -1;
    }
};



解法二:暴力

暴力第一版,时间过不了

//暴力第一版,时间过不了
class Solution2 {
public:
    int minSubArrayLen(int target, vector<int>& nums) {
        int minCnt=INT32_MAX;
        for(int i=0;i<nums.size();i++){
            for(int j=i;j<nums.size();j++){
                int sum=0;
                for(int k=i;k<=j;k++) sum+=nums[k];
                if(sum>=target&&j-i+1<minCnt) minCnt=j-i+1;
            }
        }
        if(minCnt==INT32_MAX) return 0;
        else return minCnt;
    }
};

暴力第二版,勉强过了

//暴力第二版,勉强过了
class Solution3 {
public:
    int minSubArrayLen(int target, vector<int>& nums) {
        int minCnt=INT32_MAX;
        for(int i=0;i<nums.size();i++){
            int sum=0;
            for(int j=i;j<nums.size();j++){
                sum+=nums[j];
                if(sum>=target&&j-i+1<minCnt){minCnt=j-i+1; break;}
            }
        }
        if(minCnt==INT32_MAX) return 0;
        else return minCnt;
    }
};

加了前缀和的暴力,两个没过超时

class Solution {
    public int minSubArrayLen(int target, int[] nums) {
        //前缀和
        int len = nums.length;
        int[] ints = new int[len];
        ints[0] = nums[0];
        for(int i=1;i<len;i++){
            ints[i] = nums[i] + ints[i-1];
        }
        //遍历
        int minLeft = 0;
        int minRight = 0;
        int min = Integer.MAX_VALUE;
        for(int i=0;i<len;i++){
            for(int j=i;j<len;j++){
                if(ints[j]-ints[i]+nums[i] >= target){
                    if(j-i+1<min){
                        min = j-i+1;
                        minLeft = i;
                        minRight = j;
                    }
                }
            }
        }
        //输出
        if(min == Integer.MAX_VALUE) return 0;
        else return minRight - minLeft + 1;
    }
}



解法三:滑动窗口法(O(n))

滑动窗口,就是不断的调节子序列的起始位置和终止位置,从而得出我们要想的结果,本题目思路就是:
1.每当sum>=tartget,就把窗口起始点往前移动,看看能不能缩小子数组长度,全程记录最小长度
2.窗口起始点移动到sum<tartget了,窗口终止点就继续前移,一直循环

在这里插入图片描述
在这里插入图片描述

//滑动窗口法
class Solution4 {
public:
    int minSubArrayLen(int target, vector<int>& nums) {
        int left=0;
        int right=0;
        int sum=0;
        int minCnt=INT32_MAX;
        for(right=left;right<nums.size();right++){
            sum+=nums[right];
            //注意这里使用while,每次更新left(起始位置),并不断比较子序列是否符合条件
            while(sum>=target){
                if(right-left+1<minCnt) minCnt=right-left+1;
                //这里体现出滑动窗口的精髓之处,不断变更left(子序列的起始位置)
                sum-=nums[left++];
            }
        }
        if(minCnt==INT32_MAX) return 0;
        else return minCnt;
    }
};

Java版本

class Solution {
    public int minSubArrayLen(int target, int[] nums) {
        int len = nums.length;
        int left = 0 ,right = 0;
        int minLen = Integer.MAX_VALUE;
        int sum = 0;
        for(;right<len;right++){
            sum += nums[right];
            while(sum>=target){
                if(right-left+1<minLen) minLen = right-left+1;
                sum -= nums[left];
                left++;
            }
        }
        if(minLen==Integer.MAX_VALUE) return 0;
        else return minLen;
    }
}



Leetcode904

1.问题描述

题目翻译一下:找到最长的子数组,子数组满足只有两种不同的元素,比如112221√ 12312×

在这里插入图片描述
在这里插入图片描述

2.解决方案

本体思路是滑动窗口,可以说比这一篇的滑动窗口要难一点209. 长度最小的子数组,但是居然代码写出来基本是一样的就好像模板一样,可以去翻看这一篇博客209. 长度最小的子数组
1.上题是求最短子数组,里面的while是符合条件left++,
2.本题是求最长子数组,里面的while是不符合条件left++,但是共同点都是要left++
3.上题思路是一直遍历,一旦符合条件就left++,直到不符合条件,寻求更短的符合条件子数组
4.本体思路是一直遍历,一旦不符合条件就left++,直到符合条件,寻求最长符合条件子数组
5.上题就是符合条件下尽可能短,本题就是最长的一定是在符合条件和不符合条件的边缘取符合条件!
6.至于具体思路就是一旦不符合条件就left++,一个right对应更新一次maxLength,最后返回就好,一看代码应该就懂了!

//滑动窗口法
class Solution {
public:
    int totalFruit(vector<int>& fruits) {
        int left=0,right=0;
        int maxLength=INT32_MIN;
        map<int,int> mp;
        for(right=left;right<fruits.size();right++){
            mp[fruits[right]]++;
            while(mp.size()>2){
                mp[fruits[left]]--;
                if(mp[fruits[left]]==0) mp.erase(fruits[left]);
                left++;
            }
            if(right-left+1>maxLength) maxLength=right-left+1;
        }
        return maxLength;
    }
};

Java版本

class Solution {
    public int totalFruit(int[] fruits) {
        //
        int len = fruits.length;
        int maxLen = Integer.MIN_VALUE;
        int left = 0, right = 0;
        Map<Integer, Integer> map = new HashMap<Integer, Integer>();
        //
        for(;right<len;right++){
            map.put(fruits[right], map.getOrDefault(fruits[right], 0)+1);
            while(map.size()>2){
                map.put(fruits[left], map.get(fruits[left])-1);
                if(map.get(fruits[left]) == 0) map.remove(fruits[left]);
                left++;
            }
            maxLen = Math.max(maxLen, right-left+1);
        }
        return maxLen;
    }
}



Leetcode1004

1.问题描述

在这里插入图片描述

2.解决方案

1.滑动窗口这个算法一看解析就懂了,但是我当时面字节的时候遇到这个题就完全没想到这方面
2.就是求一个子数组你不管有多少限制条件,始终就是个子数组,那么你就应该想到用滑动窗口,因为滑动窗口就是不停的选择一些子数组
3.再说这个限制条件,其实很简单就是滑动窗口总要有一个限制条件,本题就是0的个数不能多于K个,不符合条件就left++,符合条件right就一直++
4.要注意求最大值的位置一定是在每一个right的结束位置因为到这才是每一个right合法的最长的滑动窗口

在这里插入图片描述

class Solution {
public:
    int longestOnes(vector<int>& nums, int k) {
        int left=0,right=0;
        int maxlength=-1;
        int numZero=0;
        for(right=left;right<nums.size();right++){
            if(nums[right]==0) numZero++;
            while(numZero>k){
                if(nums[left]==0){
                    left++;
                    numZero--;
                }
                else left++;
            }
            //注意求最大值的位置--一定是在每一个right的结束位置因为到这才是每一个right合法的最长的滑动窗口
            maxlength= max(maxlength,right-left+1);
        }
        return maxlength;
    }
};

Java版本

class Solution {
    public int longestOnes(int[] nums, int k) {
        //1.
        int len = nums.length;
        int maxLen = Integer.MIN_VALUE;
        int cnt0 = 0;
        int left = 0, right = 0;
        //2.
        for(;right<len;right++){
            if(nums[right]==0) cnt0++;
            while(cnt0>k){
                if(nums[left]==0) cnt0--;
                left++;
            }
            maxLen = Math.max(maxLen, right-left+1);
        }
        return maxLen;
    }
}



Leetcode76

1.问题描述

在这里插入图片描述

2.解决方案

在这里插入图片描述

class Solution {
    public Boolean compareMap(Map<Character,Integer> map, Map<Character,Integer> mapT){
        //map的内容要覆盖>=mapT的内容
        for(Character key : mapT.keySet()){
            if(!map.containsKey(key) || map.get(key)<mapT.get(key)) return false;
        }
        return true;
    }
    public String minWindow(String s, String t) {
        //1.统计t
        Map<Character,Integer> mapT = new HashMap<>();
        for(int i=0;i<t.length();i++){
            Character c = t.charAt(i);
            mapT.put(c, mapT.getOrDefault(c, 0)+1);
        }
        //2.
        Map<Character,Integer> map = new HashMap<>();
        int left=0, right=0;

        int minLen = Integer.MAX_VALUE;
        int minLeft = 0, minRight = 0;

        for(;right<s.length();right++){
            Character item = s.charAt(right);
            if(mapT.containsKey(item)) map.put(item, map.getOrDefault(item, 0)+1);
            //1.覆盖所有的==map的内容要覆盖>=mapT的内容
            while(compareMap(map, mapT)){
                if(right-left+1<minLen){
                    minLen = right-left+1;
                    minLeft = left;
                    minRight = right;
                }
                Character leftChar = s.charAt(left);
                if(mapT.containsKey(leftChar)){
                    map.put(leftChar, map.get(leftChar)-1);
                    if(map.get(leftChar)==0) map.remove(leftChar);
                }
                left++;
            }
        }
        if(minLen==Integer.MAX_VALUE) return "";
        else return s.substring(minLeft, minRight+1);
    }
}
  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值