滑动窗口模板

基本思想

滑动窗口是双指针技巧的一种,定义两个指针,left 和 right,用 left,right 表示滑动窗口的左边界和右边界,通过改变left,right来扩展和收缩滑动窗口,可以想象成一个窗口在滑动。这就是滑动窗口的基本思想

使用滑动窗口前需要思考以下4个点:

  1. 增大窗口时,要更新哪些数据?
  2. 什么时候停止增大窗口,开始缩小窗口?
  3. 缩小窗口时,要更新哪些数据?
  4. 最后的结果在增大窗口时更新,还是缩小窗口时更新?

写滑动窗口算法前一定要明白以上4个点,不然根本写不出算法!!!

算法模板

抽象算法框架,具体还是得根据题干要求,慢慢补充里面的血肉之躯。

int left = 0, right = 0;

while (right < s.size()) {
    window.add(s[right]);
    right++;
    
    while (满足缩小窗口条件) {
        window.remove(s[left]);
        left++;
    }
}

一定要明白,滑动窗口算法在写代码中,代码是符合对称结构的。

应用场景

在子串和子数组中应用特别广泛:

  • 1、字符串。在一个字符串中找出符合另一个字符串的子串
  • 2、子数组。在一个数组中找到满足条件的子数组

例题应用

子串

LeetCode 3题 - 无重复字符的最长子串

class Solution {
    public int lengthOfLongestSubstring(String s) {
        
        int left = 0;
        int right = 0;
        int res = 0;
        HashMap<Character,Integer> window = new HashMap<>();
        //增大窗口
        while (right < s.length()) {
            char c = s.charAt(right);
            window.put(c,window.getOrDefault(c,0)+1);
            right++;
            //缩小窗口
            while(window.get(c)>1){              
                char c1 = s.charAt(left);
                window.put(c1,window.get(c1)-1);
                left++;
            }
             //更新结果
            res = Math.max(right-left,res);
        }
        
        return res;
    }
}

LeetCode 76题 - 最小覆盖子串

class Solution {
    public String minWindow(String s, String t) {

        if (s.length() < t.length()) {
            return "";
        }

        //滑动窗口
        HashMap<Character,Integer> window = new HashMap<>();
        
        //辅助工具
        HashMap<Character,Integer> need = new HashMap<>();
        for (char c : t.toCharArray()) {
            need.put(c,need.getOrDefault(c,0)+1);
        }

        int left = 0;
        int right = 0;

		//存储结果
        int start = 0;
        int end = 0;
        int minLen = Integer.MAX_VALUE;

        //记录 window 中已经有多少字符符合要求了
        int match = 0;

        while (right < s.length()) {
            char c = s.charAt(right);
            window.put(c,window.getOrDefault(c,0)+1);
            if((window.get(c)).equals(need.get(c))){
                match++;
            }
            right++;
            //缩小窗口
            while(match==need.size()){
                //更新结果
                if(right-left<minLen){
                    start = left;
                    end = right;
                    minLen = right-left;
                }
                char c1 = s.charAt(left);
                window.put(c1,window.get(c1)-1);
                if(need.containsKey(c1)){
                    if(window.get(c1)<need.get(c1)){
                        match--;
                    }
                }
                left++;
            }
        }
        return minLen==Integer.MAX_VALUE?"":s.substring(start, end);
    }
}

LeetCode 438题 - 找到字符串中所有字母异位词

class Solution {

    public List<Integer> findAnagrams(String s, String p) {

        //滑动窗口
        HashMap<Character,Integer> window = new HashMap<>();
        //辅助工具
        HashMap<Character,Integer> need = new HashMap<>();
        for (char c : p.toCharArray()) {
            need.put(c, need.getOrDefault(c, 0) + 1);
        }

        int left = 0;
        int right = 0;

		//记录 window 中已经有多少字符符合要求了
        int match = 0;

        //存储结果
        List<Integer> res = new ArrayList<>();

        while (right < s.length()) {
            char c = s.charAt(right);
            window.put(c, window.getOrDefault(c, 0) + 1);
            if(need.containsKey(c)){  
                if(window.get(c).equals(need.get(c))){
                    match++;
                }
            }
            right++;
            while (match == need.size()) {
                //更新结果
                if (right - left == p.length()) {
                    res.add(left);
                }
                char c1 = s.charAt(left);
                window.put(c1,window.get(c1)-1);
                if (need.containsKey(c1)) {
                    if (window.get(c1) < need.get(c1)) {
                        match--;
                    }
                }
                left++;
            }
        }
        return res;
    }
}

LeetCode 567题 - 字符串的排列

class Solution {
    public boolean checkInclusion(String s1, String s2) {

        //滑动窗口
        HashMap<Character,Integer> window = new HashMap<>();
        //辅助
        HashMap<Character,Integer> need = new HashMap<>();
        for (char c : s1.toCharArray()) {
            need.put(c, need.getOrDefault(c, 0) + 1);
        }

        int left = 0;
        int right = 0;

		//记录 window 中已经有多少字符符合要求了
        int match = 0;
        
        while (right < s2.length()) {
            char c = s2.charAt(right);
            window.put(c, window.getOrDefault(c, 0) + 1);
            if (need.containsKey(c)) {
                if(window.get(c).equals(need.get(c))){
                    match++;
                }
            }
            right++;
            while (match == need.size()) {
                //更新结果
                if (right - left == s1.length()) {
                    return true;
                }
                char c1 = s2.charAt(left);
                window.put(c1, window.get(c1) - 1);
                if(need.containsKey(c1)){ 
                    if (window.get(c1) < need.get(c1)) {
                        match--;
                    }
                }
                left++;
            }
        }
        return false;
    }
}

子数组

LeetCode 209题 - 长度最小的子数组

class Solution {
    public int minSubArrayLen(int target, int[] nums) {

        int left = 0;
        int right = 0;
        int sum = 0;
        int minLen = Integer.MAX_VALUE;

        while (right < nums.length) {
            int num = nums[right];
            sum += num;
            right++;
            while (sum >= target) {
                //更新结果
                if (right - left < minLen) {
                    minLen = right - left;
                }
                int num1 = nums[left];
                sum -= num1;
                left++;
            }
        }
        return minLen==Integer.MAX_VALUE?0:minLen;
    }
}

剑指offer - 和为S的连续正数序列

class Solution {
    public int[][] findContinuousSequence(int target) {

        //存储结果
        List<int[]> res = new ArrayList<>();

        int left = 1;//注意
        int right = 1;//注意
        int sum = 0;
        //为什么是target/2 +1?其实只要求target前半部分就可以了,后面部分的target加起来一定是大于target的。不加1会存在漏解的情况
        while (right <= (target / 2 + 1)) {
            int num = right;
            sum += right;
            right++;
            while (sum >= target) {
                //更新结果
                if (sum == target) {
                    int[] temp = new int[right - left];
                    for (int i = 0; i < temp.length; i++) {
                        temp[i] = left + i;
                    }
                    res.add(temp);
                }
                int num1 = left;
                sum -= num1;
                left++;
            }
        }
        return res.toArray(new int[res.size()][]);
    }
}

参考资料

我写了套框架,把滑动窗口算法变成了默写题
滑动窗口算法框架

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值