滑动窗口专题

目录

滑动窗口是什么


滑动窗口是什么

滑动窗口技术是一种强大的算法策略,主要用于解决涉及连续子数组或子字符串的问题,尤其是当这些问题要求优化时间复杂度时。它非常适合于解决以下类型的题型:

  1. 固定长度的窗口问题

    • 当要求处理长度固定的连续子数组或子字符串的问题时,滑动窗口可以按照固定的长度向前移动,计算每个窗口的目标值。例如,计算每个长度为k的连续子数组的平均值。
  2. 可变长度的窗口问题

    • 对于那些没有固定长度要求的子数组或子字符串问题,滑动窗口技术尤其有效。窗口的大小根据当前的需求动态调整,这类问题包括但不限于:
      • 找到包含(或不包含)特定元素的最小(或最大)子数组或子字符串。
      • 计算满足某些条件的最长或最短子数组或子字符串的长度。
      • 找到所有满足特定条件的子数组或子字符串。
  3. 计数问题

    • 滑动窗口可以用来计数,例如,计算一个字符串中包含所有字符的最短子字符串的数量。
  4. 求和问题

    • 当需要找出数组中的子数组,使其和等于、小于或大于给定值时,滑动窗口可以有效地进行处理。
  5. 字符处理问题

    • 滑动窗口经常用于处理字符串问题,如计算不含重复字符的最长子串的长度,或找到包含所有给定字符的最短子串。
  6. 双指针问题的变体

    • 某些双指针问题也可以视为滑动窗口问题的变体,特别是当问题涉及到连续子序列时。

滑动窗口技术之所以强大,是因为它利用了窗口的边界来减少不必要的计算,通过只在窗口的一侧添加或删除元素,它能够在不重新遍历整个数组或字符串的情况下,快速更新窗口内的信息。这种方法通常能将时间复杂度从O(n^2)优化到O(n),使其成为解决这类问题的高效策略。

 

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

给定一个字符串 s ,请你找出其中不含有重复字符的 最长子串的长度。

示例 1:

输入: s = "abcabcbb"
输出: 3 
解释: 因为无重复字符的最长子串是 "abc",所以其长度为 3。
import java.util.HashMap;
import java.util.Map;

class Solution {
    public int lengthOfLongestSubstring(String s) {
        // 使用HashMap来存储字符及其最后一次出现的索引
        Map<Character, Integer> map = new HashMap<>();
        // 初始化滑动窗口的起始位置和最大长度
        int start = 0, maxLength = 0;
        
        // 遍历字符串
        for (int i = 0; i < s.length(); i++) {
            char c = s.charAt(i);
            // 如果当前字符已存在于HashMap中,说明之前出现过,需要更新滑动窗口的起始位置
            // 新的起始位置是当前字符上一次出现位置的下一个位置与当前start的较大值
            // 这是为了保证窗口只向前移动,避免回退
            if(map.containsKey(c)){
                start = Math.max(map.get(c) + 1, start);
            }
            
            // 更新当前字符的索引
            map.put(c, i);
            
            // 计算当前无重复字符子串的长度(i - start + 1),并更新最大长度
            maxLength = Math.max(i - start + 1, maxLength);
        }
        
        // 返回最大长度
        return maxLength;
    }
}
  • HashMap (map): 用于存储遍历过程中字符及其最后一次出现的索引。这样我们可以快速检查当前字符是否已经在之前出现过,以及它的上一次出现位置。

  • 滑动窗口 (start, i): start变量表示当前考虑的不含重复字符子串的起始位置,i表示遍历到的当前位置,它们共同定义了滑动窗口的边界。

  • 更新窗口起始位置: 如果当前字符c已经在之前的位置出现过(即map中已有c的记录),则需要更新start以确保窗口内的字符串不包含重复字符。start更新为map.get(c) + 1(即当前字符上一次出现位置的下一个位置)和当前start的较大值,以保证窗口始终向前滑动,不回退。

  • 更新最大长度 (maxLength): 在每次迭代中,都会计算当前窗口的长度(i - start + 1),并与已知的最大长度比较,更新最大长度。

  • 返回结果: 遍历完成后,maxLength即为所求的最长无重复字符子串的长度。

重点:start的更新——如何防止滑动窗口回退

假设我们有字符串 s = "tmsmartz",我们按顺序处理字符串以找到不含有重复字符的最长子串。

  1. 开始时, start = 0, 我们逐个处理字符并将它们及其索引存储在HashMap中。

    • end = 3, 字符串为 "tmsm",此时我们遇到了重复字符 'm'。map.get('m') 返回 1,因为 'm' 最初出现在索引 1 的位置。根据直接跳转的逻辑,我们会将 start 设置为 map.get('m') + 1 = 2
  2. 接下来, 我们继续移动end,直到 end = 6,此时字符串为 "tmsmart",遇到了重复字符 't'。map.get('t') 返回 0,因为 't' 最初出现在索引 0 的位置。

现在,如果我们按照直接跳转到 map.get('t') + 1 的逻辑,即 1,这实际上比当前的 start(在第一步后为 2)还要小。如果我们更新 start1,那么窗口就会回退到包含两个 'm' 的位置,从 "smart" 回退到 "tms",这显然是错误的,因为我们已经移除了第一个 'm' 来处理第二个 'm' 的重复情况。

为了避免这种回退,我们使用 start = Math.max(map.get(currentChar) + 1, start)。所以在处理第二个 't' 时,即使 map.get('t') + 1 = 1,我们也会保持 start = 2,因为 Math.max(1, 2) = 2。这样确保了窗口只会向前移动或保持不变,不会发生回退,保持算法的正确性。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值