滑动窗口算法

目录

核心思想

常见类型及应用场景

1. 固定大小窗口

示例:长度为 k 的子数组的最大和

示例:找到字符串中所有字母异位词

2. 可变大小窗口

示例:无重复字符的最长子串

滑动窗口的优势

适用场景总结

滑动窗口算法是一种基于双指针的高效处理技巧,主要用于解决数组或字符串中连续子序列 / 子串的问题。它通过维护一个动态变化的 "窗口"(由左右两个指针界定的连续区间),在遍历过程中灵活调整窗口大小,从而将原本需要嵌套循环(O (n²) 复杂度)的问题优化为线性时间(O (n) 复杂度)。

核心思想

  • 窗口定义:由两个指针left(左边界)和right(右边界)界定的连续区间[left, right]

  • 滑动逻辑

    • 先移动right指针扩展窗口[left, right],直到满足某个条件。

    • 再移动left指针收缩窗口,直到不满足条件,同时更新最优解。

    • 重复上述过程,直到right遍历完整个序列。

常见类型及应用场景

滑动窗口主要分为固定大小窗口可变大小窗口两类:

1. 固定大小窗口

窗口大小固定(如长度为k),常用于求解 " 长度为k的子数组的最大和 / 平均值 " 等问题。

示例:长度为 k 的子数组的最大和

问题:给定数组nums和整数k,找到长度为k的连续子数组的最大和。

解法步骤

  • 初始化窗口:计算前k个元素的和作为初始窗口和。

  • 滑动窗口:从第k个元素开始,每次移动窗口时,减去离开窗口的元素(nums[i-k]),加上进入窗口的元素(nums[i]),更新最大和。

i 的范围只能从下标0到(array.lenght-1)-k,而j的范围可以从0到array.lenght-1,就这样形成一个为k的大小窗口,左为i,右为j=i+k

public static void maxOf(int[] array,int k){
         int sum=0;
         for(int i=0;i<=array.length-k;i++){
             int sum2=0;
             for(int j=i;j<j+k;j++){
                 sum2+=array[j];
             }
             sum=Math.max(sum,sum2);
         }
         System.out.println(sum);
     }
示例:找到字符串中所有字母异位词

问题:给定两个字符串 sp,找到 s 中所有 p异位词 的子串,返回这些子串的起始索引。不考虑答案输出的顺序。

解法步骤

  • 初始化阶段:创建两个长度为26的数组 sCountpCount,用于统计字符出现频次,分别统计字符串 s 前 plen 个字符和字符串 p 所有字符的频次

  • 检查初始窗口:比较初始窗口的字符频次是否与目标字符串一致,如果一致,则将位置0加入结果列表

  • 滑动窗口遍历:窗口从左向右滑动,每次移动一位,移除窗口左侧字符,添加窗口右侧新字符,比较当前窗口与目标字符串的字符频次

class Solution {
     public List<Integer> findAnagrams(String s, String p) {
         List<Integer> res=new ArrayList<>();
         int slen=s.length();
         int plen=p.length();
         if(slen<plen){
             return res;
         }
         List<Integer> ans=new ArrayList<>();
         int[] sCount=new int[26];
         int[] pCount=new int[26];
         for(int i=0;i<plen;i++){
             ++sCount[s.charAt(i)-'a'];
             ++pCount[p.charAt(i)-'a'];
         }
         if(Arrays.equals(sCount,pCount)){
             ans.add(0);
         }
         for(int i=0;i<slen-plen;i++){
             --sCount[s.charAt(i)-'a'];
             ++sCount[s.charAt(i+plen)-'a'];
             if(Arrays.equals(sCount,pCount)){
                 ans.add(i+1);
             }
         }
         return ans;
     }
 }
2. 可变大小窗口

窗口大小不固定,需根据条件动态调整,常用于求解 "最长 / 最短子串 / 子数组" 等问题(如无重复字符的最长子串、最小覆盖子串)。

示例:无重复字符的最长子串

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

解法步骤

  • leftright标记窗口边界,初始均为 0。

  • 移动right指针,将字符加入窗口,并用哈希表记录字符最后出现的位置。

  • right指向的字符已在窗口中(即哈希表记录的位置≥left),则将left移动到该字符上次出现位置的下一位(确保窗口内无重复)。

  • 每次移动right后,更新最长子串长度。

public class T003 {
    public static void main(String[] args) {
        String s = "abcabcbb";
        System.out.println(lengthOfLongestSubstring(s));
    }
 public static int lengthOfLongestSubstring(String s) {
         // 哈希集合,记录每个字符是否出现过
         Set<Character> occ = new HashSet<Character>();
         int n = s.length();
         // 右指针,初始值为 -1,相当于我们在字符串的左边界的左侧,还没有开始移动
         int rk = -1, ans = 0;
         for (int i = 0; i < n; i++) {
             if(i!=0){
                 // 左指针向右移动一格,移除一个字符
                 occ.remove(s.charAt(i-1));
             }
             // 扩展窗口右边界,直到遇到重复字
             while(rk+1<n&&!occ.contains(s.charAt(rk+1))){
 //                不断地移动右指针
                 occ.add(s.charAt(rk+1));
                 rk++;
             }
 ​
 //            第i到rk个字符是一个极长的无重复字符字串
             ans=Math.max(ans,rk-i+1);
         }
 ​
         return ans;
     }
}

滑动窗口的优势

  • 时间效率高:将嵌套循环的 O (n²) 复杂度优化为 O (n),只需一次遍历。

  • 空间复杂度低:通常只需 O (1) 或 O (k) 的额外空间(k为字符集大小)。

适用场景总结

滑动窗口适用于连续子序列 / 子串问题,典型场景包括:

  • 子数组的最大 / 最小和、平均值(固定窗口)。

  • 最长 / 最短子串(如无重复字符、包含特定字符等,可变窗口)。

  • 字符串匹配(如最小覆盖子串、找到所有字母异位词)。

核心是通过灵活调整窗口边界,在遍历过程中动态维护窗口状态,避免重复计算,从而高效求解问题。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值