滑动窗口做题思路

什么是滑动窗口?就是一个队列,然后通过在这个队列中的各种移除和添加满足题目需求

题目:

209. 长度最小的子数组 - 力扣(LeetCode)

 

class Solution {
    public int minSubArrayLen(int target, int[] nums) {
        int left = 0;
        int sum = 0;
        int n = nums.length;
        int ret = n+1;
        for(int i = 0;i<n;i++){
            sum+=nums[i];
            while(sum-nums[left] >= target){
                sum= sum-nums[left++];
            }
            if(sum>=target){
                ret = Math.min(ret,i-left+1);
            }
        }
        return ret<=n?ret:0;
    }
}

复杂度分析
时间复杂度:O(n)
空间复杂度:O(1)

713. 乘积小于 K 的子数组 - 力扣(LeetCode)

class Solution {
    public int numSubarrayProductLessThanK(int[] nums, int k) {
        //滑动窗口
        //l  r
        //[l,r] [l+1,r]...[r,r]
        //r-1+1
        if(k<=1){
            return 0;
        }
        int n = nums.length,ans = 0,prod = 1,left = 0;
        for(int right = 0;right<n;++right){
            prod *= nums[right];
            while(prod>=k){
                prod/=nums[left++];

            }
            ans+=right-left+1;
        }
        return ans;
    }
}

本题采用的是双指针滑动窗口,大循环是右指针的移动,内部小循环是左指针的移动。

时间复杂度:O(n),其中 n 是数组 nums 的长度,右指针遍历一遍数组,左指针紧随其后最多遍历一遍数组;

空间复杂度:O(1),只创建了有限的几个常量变量作为记录。

1004. 最大连续1的个数 III - 力扣(LeetCode)

 滑动窗口 + 变量计数模板

class Solution {
    public int slidingWindow(int[] nums, int k) {
        //数组/字符串长度
        int n = nums.length;
        //双指针,表示当前遍历的区间[left, right],闭区间
        int left = 0, right = 0;
        //定义变量统计 子数组/子区间 是否有效
        int sum = 0;
        //定义变量动态保存最大 求和/计数
        int res = 0;

        //右指针遍历到数组尾
        while (right < n) {
            //增加当前右指针对应的数值
            sum += nums[right];
            //当在该区间内 sum 超出定义范围
            while (sum > k) {
                //先将左指针指向的数值减去
                sum -= nums[left];
                //左指针右移
                left++;
            }
            //到 while 结束时,我们找到了一个符合题意要求的 子数组/子串
            res = Math.max(res, right - left + 1);
            //移动右指针,去探索下一个区间
            right++;
        }
        return res;
    }
}

 

class Solution {
    public int longestOnes(int[] nums, int k) {
        int n = nums.length;
        int left = 0;
        int right = 0;
        int zeros = 0;
        int len=0;

        while(right<n){
            if(nums[right]==0){
                zeros++;
            }
            //当0个数超了,让left一直右移到满足
            while(zeros>k){
                if(nums[left]==0){
                    zeros--;
                }
                left++;
            }
            //记录符合条件的长度
            len = Math.max(len,right-left+1);
            right++;
        }
        return len;
    }
}

567. 字符串的排列 - 力扣(LeetCode)

72ms

class Solution {
    public boolean checkInclusion(String s1, String s2) {
            if (s1.length() > s2.length()) {
        return false;
    }

    int[] count1 = new int[26];
    // 统计s1中每个字符出现的次数
    for (int i = 0; i < s1.length(); i++) {
        char c = s1.charAt(i);
        count1[c - 'a']++;
    }

    // 定义滑动窗口的范围是[l,r],闭区间,长度与s1相等
    int l = 0, r = s1.length();

    while (r <= s2.length()) {
        // 统计窗口s2[l,r-1]内的元素出现的次数
        int[] count2 = new int[26];
        for (int i = l; i < r; i++) {
            char c = s2.charAt(i);
            count2[c - 'a']++;
        }

        // 如果滑动窗口内各个元素出现的次数跟 s1 的元素出现次数完全一致,返回 True
        if(Arrays.equals(count1,count2)) {
            return true;
        }

        l++;
        r++;
    }
    return false;

    }
}

效率很低23ms

class Solution {
    public boolean checkInclusion(String s1, String s2) {
        //getOrDefault(Object key, Object defaultValues)
        //若map中存在key 则返回key对应的value值
       //否则返回默认值defaultValues

       //滑动窗口 + 两哈希,始终保证窗口长度,当长度超了,左指针准备右移

       Map<Character,Integer>need = new HashMap<>();
       Map<Character,Integer>map = new HashMap<>();

       //创建[双指针] 和 [有效变量]
       int left = 0,right = 0;
       int valid = 0;

       for(Character c:s1.toCharArray()){
            need.put(c,need.getOrDefault(c,0)+1);
       }
       while(right<s2.length()){
            char c = s2.charAt(right);
            if(need.containsKey(c)){
                map.put(c,map.getOrDefault(c,0)+1);
                if(need.get(c).equals(map.get(c))){
                    valid++;
                }
            }
            //当凑齐了元素,还要判断长度
            while(right-left+1>=s1.length()){
                if(valid==need.size()){
                    return true;
                }
                char d = s2.charAt(left);
                if(need.containsKey(d)){
                    if(need.get(d).equals(map.get(d))){
                        valid--;
                    }
                    map.put(d,map.get(d)-1);
                }
                left++;
            }
            right++;

       }
       return false;
    }
}

6ms 

#define MAX_CHAR_LENGTH 26
bool isMatch(int*hash1,int *hash2)
{
    for(int i = 0;i<MAX_CHAR_LENGTH;i++){
        if(hash1[i]!=hash2[i])return false;
    }
    return true;
}
bool checkInclusion(char* s1, char* s2) {
    if(strlen(s1)>strlen(s2))return false;
    int len1 = strlen(s1);
    int len2 = strlen(s2);
    int windowSize = len1;
    int hash1[MAX_CHAR_LENGTH];
    int hash2[MAX_CHAR_LENGTH];
    memset(hash1,0,sizeof(hash1));
    memset(hash2,0,sizeof(hash2));
    //初始化hash1,hash2
    for(int i = 0;i<windowSize;i++){
        hash1[s1[i]-'a']++;
        hash2[s2[i]-'a']++;
    }
    //首次匹配
    if(isMatch(hash1,hash2))return true;
    //s2的滑动窗口,每次右移一位,更新hash2
    for(int i = len1;i<len2;i++){
        hash2[s2[i]-'a']++;
        hash2[s2[i-len1]-'a']--;
        if(isMatch(hash1,hash2))return true;
    }
    return false;
}

5ms 

class Solution {
    public boolean checkInclusion(String s1, String s2) {
        int n = s1.length(),m = s2.length();
        if(n>m){
            return false;
        }
        int[] cnt1 = new int[26];
        int[] cnt2 = new int[26];
        for(int i =0;i<n;i++){
            ++cnt1[s1.charAt(i)-'a'];
            ++cnt2[s2.charAt(i)-'a'];
        }
        if(Arrays.equals(cnt1,cnt2)){
            return true;
        }
        for(int i=n;i<m;i++){
            ++cnt2[s2.charAt(i)-'a'];
            --cnt2[s2.charAt(i-n)-'a'];
            if(Arrays.equals(cnt1,cnt2)){
                return true;
            }
        }
        return false;
    }
}

 

3. 无重复字符的最长子串 - 力扣(LeetCode)

import java.util.HashMap;

/**
 * <h2>无重复字符的最长子串</h2>
 * <p>1.给定一个字符串s,请你找出其中不含有重复字符的最长子串的长度.</p>
 * <p>2.s由英文字母,数字,符号和空格组成</p>
 */
public class Leetcode03 {

    public int lengthOfLongestSubstring(String s){
        HashMap<Character,Integer> map = new HashMap<>();//key返回比较大用hashMap 但是这道题目仅仅是128个字符可以用数组
        int begin = 0;
        int maxLength = 0;
        for(int end = 0;end<s.length();end++){
            char ch = s.charAt(end);
            if (map.containsKey(ch)) {//重复时,调整begin
                begin = Math.max(begin,map.get(ch) + 1);//防止begin回退
                map.put(ch,end);
            }else{//没有重复
                map.put(ch,end);
            }
//            System.out.println(s.substring(begin,end+1));
            maxLength = Math.max(maxLength,end - begin +1);
        }
        return maxLength;
    }

    public static void main(String[] args){
//        System.out.println(new Leetcode03().lengthOfLongestSubstring("abcabcbb"));
        Leetcode03 e02 = new Leetcode03();
//        System.out.println(e02.lengthOfLongestSubstring("abcabcbb"));
        System.out.println(e02.lengthOfLongestSubstring("abba"));
        /*
            [(a,0),(b,2)]
             b
               e
            abba
         */



         /*
            a
            ab
            abc
            bca
            cab
            abc
            cb
            b
          */

        /*
            给定一个字符串 s ,请你找出其中不含重复字符的最长子串的长度
            abcabcbb   3
            a
            ab
            abc
            bca
            cab
            abc
            cb
            b

            bbbbbb      1
            b

            pwwkew      3
            p
            pw
            w
            wk
            wke
            kew

            [(a,3),(b,7),(c,5)]
                 b
                   e
            abcabcbb

        要点:
            1.用 begin 和 end 表示子串开始和结束位置
            2.用hash表检查重复字符
            3.从左到右查看每一个字符,如果:
                - 没遇到重复字符,调整end
                - 遇到重复的字符,调整begin
                - 将当前字符放入hash表
            4.end - begin + 1 是当前子串长度
         */

    }
}

 76. 最小覆盖子串 - 力扣(LeetCode)

 16ms

class Solution {
    public String minWindow(String S, String t) {
        //这道题目大写小写都有直接创建128大小的数组,保证所有ASCII字符都可以统计
        char[] s = S.toCharArray();
        int m = s.length;
        int ansLeft = -1;
        int ansRight=m;
        int left = 0;
        int[] cntS =new int[128];//s子串字母出现次数
        int[] cntT = new int[128];//t中字母出现次数
        for(char c:t.toCharArray()){
            cntT[c]++;
        }
        for(int right =0;right<m;right++){//移动子串右端点
            cntS[s[right]]++;//右端点字母移入子串
            while(isCovered(cntS,cntT)){//涵盖
                if(right-left<ansRight-ansLeft){//找到更短的子串
                    ansLeft = left;
                    ansRight =right;
                }
                cntS[s[left++]]--;//左端点字母移出子串
            }
        }
        return ansLeft<0?"":S.substring(ansLeft,ansRight+1);
     }
     private boolean isCovered(int[] cntS,int[] cntT){
        for(int i ='A';i<='Z';i++){
            if(cntS[i] < cntT[i]){
                return false;
            }

        }
        for(int i ='a';i<='z';i++){
            if(cntS[i] < cntT[i]){
                return false;
            }

        }
        return true;
        
     }
}

 13ms

class Solution {
    public String minWindow(String s, String t) {
        // 参数校验
        if (s == null || t == null || s.length() < t.length()) {
            return "";
        }
        // 保存t字符出现的次数,表示每个字符待需要的数量
        Map<Character, Integer> tMap = new HashMap<>(t.length());
        for (int i = 0; i < t.length(); i++) {
            tMap.put(t.charAt(i), tMap.getOrDefault(t.charAt(i), 0) + 1);
        }
        // 判断窗口是否包含了t的所有元素,避免每次去遍历tMap,增加耗时
        int needCnt = t.length();
        // 定义最小结果集时的左右边界,初始化边界长度为最大值
        int[] minResult = new int[]{0, Integer.MAX_VALUE};
        int length = s.length();
        // 定义滑动窗口左右边界,右边界开始移动
        for (int i = 0, j = 0; j < length; j++) {
            char c = s.charAt(j);
            // 包含t,待需要的数量减一
            if (tMap.getOrDefault(c, 0) > 0) {
                needCnt--;
            }
            // 同时map需要的字符数量减一
            tMap.put(c, tMap.getOrDefault(c, 0) - 1);
            // 都包含了,此时右边界j不动,开始移动左边界,尝试缩小窗口
            if (needCnt == 0) {
                
                while (true) {
                    c = s.charAt(i);
                    // 左边界字符已经满足了,不再需要了,则退出循环,没办法继续缩小了
                    // 否则继续缩小,那么会执行下面的+1,所需要的字符又增加了
                    if (tMap.get(c) == 0) {
                        break;
                    }
                    // 左边界字符
                    // map还有多余数量可以缩小
                    // 缩小左边界,该字符所需要的数量+1
                    tMap.put(c, tMap.getOrDefault(c, 0) + 1);
                    i++;
                }
                // 左边界也更新完了,这时该更新结果了,覆盖res
                if (j - i < minResult[1] - minResult[0]) {
                    minResult[1] = j;
                    minResult[0] = i;
                }
                c = s.charAt(i);
                //将左边界右移,执行下一个窗口
                // 由于左边界是t需要的字符,右移后,需要更新tMap和needCnt,表示还需要增加一个字符
                tMap.put(c, tMap.getOrDefault(c, 0) + 1);
                needCnt++;
                i++;
            }
        }
        // 超过边界,返回空串
        if (minResult[1] > length) {
            return "";
        } else {
            return s.substring(minResult[0], minResult[1] + 1);
        }
    }
}

  • 5
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值