一套模板秒杀滑动窗口 & leetcode滑动窗口题目汇总

滑动窗口题目模板

step1:根据题意维护变量

  • 和 Sum
  • 最大长度 max_len,最小长度 min_len
  • 不重复 hashmap = {}

step2: 窗口的开始位置start和结束位置end

step3: 根据条件写判断语句,维护step1中的变量

step4: 根据题目要求,从中选择一种方法套用

  • 选择一:窗口长度固定,如果窗口长度达到限定的长度:
    • 1.更新step1中的相关变量
    • 2.窗口左边位置start向前移动 1,保证end向右移动时窗口长度保持不变
  • 选择二:窗口长度不固定,则 while 窗口条件不符合:
    • 1.更新step1中的相关变量
    • 2.不断移动start,直到窗口条件符合

step5:返回答案

3. 无重复字符的最长子串

解题思路:题目中提到不重复字符串,所以引入哈希表来维护窗口

思路一:key存储元素,value存储出现的次数

这种方式是用过存储key出现的次数来维护一个窗口:

  • end - start + 1 == window.size() 说明 [start,end] 内的元素没有重复的;
  • end - start + 1 >= window.size() 说明 [start,end] 内 出现了重复元素,此时需要做处理。
public int lengthOfLongestSubstring(String s) {
    int start = 0,maxLen = 0;
    Map<Character,Integer> window = new HashMap<>();
    for (int end = 0; end < s.length(); end++) {
        char c = s.charAt(end);
        window.put(c,window.getOrDefault(c,0)+1);
        /*
        1.end - start + 1 == window.size() 说明 [start,end] 内的元素没有重复的
        2.end - start + 1 >= window.size() 说明 [start,end] 内出现了重复法元素
         */
        if (end - start + 1 == window.size()) {
            maxLen = Math.max(maxLen,window.size());
        }

        while (end - start + 1 > window.size()) {
            char temp = s.charAt(start);
            window.put(temp,window.get(temp) - 1);
            if (window.get(temp) == 0) {
                window.remove(temp);
            }
            start++;
        }

    }
    return maxLen;
}

思路二:key存储元素,value存储该元素最后出现的下标(推荐使用)

这种方式是用过存储key最后出现的下标来维护一个窗口

public int lengthOfLongestSubstring(String s) {
    int start = 0,maxLen = 0;
    Map<Character,Integer> window = new HashMap<>();
    for (int end = 0; end < s.length(); end++) {
        char c = s.charAt(end);
        if (window.containsKey(c)) {
            start = Math.max(window.get(c) + 1,start);
        }
        maxLen = Math.max(maxLen,end - start + 1);
        window.put(c,end);
    }
    return maxLen;
}

大家可能会疑惑为什么要写成 start = Math.max(window.get(c) + 1,start);,而不写成 start = window.get(c) + 1,下面我们以 "abba" 为例来分析一下,并且假设 start = window.get(c) + 1

  • 步骤一:首先map中会存入 a : 0,b : 1;
  • 步骤二:遍历到第二个 b 的时候,start = 1 + 1 = 2;
  • 步骤三:遍历到第二个 a 的时候,start = 0 + 1 = 1(但是在 步骤二 的时候 start 已经为 2 了,问题就出在这里),此时继续往下的话 maxLen = 3 - 1 + 1 = 3,而不是正确答案 2 了。

如果是 start = Math.max(window.get(c) + 1,start);,那么在第三步的时候,start = Math.max(1,2) = 2,则结果为 maxLen = 3 - 2 + 1 = 2,此时是正确结果。

1695. 删除子数组的最大得分

思路一:key存储元素,value存储出现的次数

这种方式是用过存储key出现的次数来维护一个窗口

public int maximumUniqueSubarray(int[] nums) {
    int start = 0,sum = 0,maxVal = Integer.MIN_VALUE;
    Map<Integer,Integer> window = new HashMap<>();

    for (int end = 0; end < nums.length; end++) {
        int temp = nums[end];
        sum += temp;
        window.put(temp,window.getOrDefault(temp,0) + 1);
        if (end - start + 1 == window.size()) {
            maxVal = Math.max(maxVal,sum);
        }
        while (end - start + 1 > window.size()) {
            window.put(nums[start],window.get(nums[start]) - 1);
            if (window.get(nums[start]) == 0) {
                window.remove(nums[start]);
            }
            sum -= nums[start];
            start++;
        }
    }
    return maxVal;
}

思路二:key存储元素,value存储该元素最后出现的下标(推荐使用)

这种方式是用过存储key最后出现的下标来维护一个窗口

public int maximumUniqueSubarray(int[] nums) {
    int start = 0,sum = 0,maxVal = Integer.MIN_VALUE;
    Map<Integer,Integer> window = new HashMap<>();

    for (int end = 0; end < nums.length; end++) {

        if (window.containsKey(nums[end])) {
            int temp = start;
            start = Math.max(window.get(nums[end]) + 1,start);
            while (temp != start) {
                sum -= nums[temp++];
            }
        }
        sum += nums[end];
        maxVal = Math.max(maxVal,sum);
        window.put(nums[end],end);
    }
    return maxVal;
}

为什么要写成 start = Math.max(window.get(nums[end]) +1,start);,而不是 start = window.get(nums[end]) +1;,之前已经讲过了。

438. 找到字符串中所有字母异位词(重要)

这里利用数组结合双指针来维护一个窗口,非常巧妙,需要多加理解。

public List<Integer> findAnagrams(String s, String p) {
    int start = 0,end = 0;
    List<Integer> result = new ArrayList<>();
    int[] count = new int[26];
    for (int i = 0; i < p.length(); i++) {
        count[p.charAt(i) - 'a']++;
    }
    while (end < s.length()) {
        if (count[s.charAt(end) - 'a'] > 0) {
            count[s.charAt(end++) - 'a']--;
            if (end - start == p.length()) {
                result.add(start);
            }
        } else {
            count[s.charAt(start++) - 'a']++;
        }
    }

    return result;
}

567. 字符串的排列

本题思路和上一题相同。

public boolean checkInclusion(String s1, String s2) {
    int[] count = new int[128];
    for (char c : s1.toCharArray()) {
        count[c]++;
    }
    int start = 0,end = 0;
    while (end < s2.length()) {
        if (count[s2.charAt(end)] > 0) {
            count[s2.charAt(end++)]--;
            if (end - start == s1.length()) {
                return true;
            }
        } else {
            count[s2.charAt(start++)]++;
        }
    }
    return false;
}

其它题目

209. 长度最小的子数组

解题思路:通过滑动窗口算法找到连续子数组的和 >=target的最小长度

public int minSubArrayLen(int target, int[] nums) {
    int start = 0,sum = 0,minLen = Integer.MAX_VALUE;
    for (int end = 0; end < nums.length; end++) {
        sum += nums[end];
        while (sum >= target) {
            minLen = Math.min(end - start + 1,minLen);
            sum -= nums[start++];
        }
    }
    return minLen == Integer.MAX_VALUE ? 0 : minLen;
}

643. 子数组最大平均数 I

解题思路:通过滑动窗口算法找到长度为k的连续子数组的最大和,再除以k,得到结果

public double findMaxAverage(int[] nums, int k) {
    int max = Integer.MIN_VALUE;
    int sum = 0;
    int start = 0;
    for (int end = 0; end < nums.length; end++) {
        sum += nums[end];
        if (end - start + 1 == k) {
            max = Math.max(sum,max);
            sum -= nums[start++];
        }
    }
    return 1.0 * max / k;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

熠熠98

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值