滑动窗口算法

文章目录

  • 前言
  • 滑动窗口
  • 总结


前言

在引入滑动窗口的概念之前,我们先看一道例题:

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

拿到这道题最容易想到的解法就是暴力枚举。即从字符 a 1 a_1 a1开始遍历,当遇到与 a 1 a_1 a1相同的字符 a 2 a_2 a2时,记录下最长子串长度,指针再回到 a 1 a_1 a1字符的下一个位置,重复上述流程…
代码如下:

public int lengthOfLongestSubstring(String s) {
    // 转换为字符数组
    char[] arr = s.toCharArray();
    // 哈希表记录是否重复
    HashMap<Character,Integer> map = new HashMap<>();
    // 记录最长长度
    int len = 0;
    int max = 0;
    for (int i = 0; i < arr.length; i++) {
        // 如果已经存在这个字符,记录长度,并重新从这个字符的下一个位置开始遍历
        if(map.containsKey(arr[i])){
            max = Math.max(max,len);
            len = 1;
            i = map.get(arr[i])+1;
            map.clear();
            map.put(arr[i],i);
            continue;
        }
        map.put(arr[i],i);
        len++;
        max = Math.max(max,len);
    }
    return max;
}

这种解法可以说朴实无华且枯燥,仔细分析就会发现,在遍历的过程中其实做了很多无用功:
滑动窗口算法_第1张图片
例如在上图中,我们通过一轮遍历,找到了红框圈出的最长无重复子串,然后指针又回溯到了第一个b字符的位置。但其实绿框圈出的部分,我们在上一轮遍历过程中就已经得知,其中必定没有重复字符。然而在后续的过程中,程序还是遍历了它们。那么如何记录下我们已经遍历过的,没有重复的子串呢?这就要请出我们的主角——滑动窗口。


滑动窗口

顾名思义,滑动窗口就是一个动态的窗口,这个窗口可以是固定长度,也可以是可变长度,随具体问题而定。在这道题目中,我们可以将遍历过的不重复的子串留在窗口中,这样就只需要关心窗口边缘的字符就可以了。下面是图解:
滑动窗口算法_第2张图片
代码如下:


public int lengthOfLongestSubstring(String s) {
    // 哈希表记录字符出现的位置
    HashMap<Character,Integer> map = new HashMap<>();
    // 左指针
    int left = 0;
    // 记录最大长度
    int max = 0;
    // for循环指针充当右指针
    for (int right = 0; right < s.length(); right++) {
        // 当前字符
        char cur = s.charAt(right);
        // 如果遇到重复字符,左指针直接移动到该字符的下一位
        // 注意因为map中存储的值一直存在,所以要确保左指针不会回退
        if(map.containsKey(cur)){
            left = Math.max(map.get(cur)+1,left);
        }
        // 更新最大长度值
        max = Math.max(max,right - left+1);
        // 更新哈希表中映射的下标(遇到重复值后更新成新的位置)
        map.put(cur,right);
    }
    return max;
}

总结

这道题属于滑动窗口比较简单的应用,后续再遇到滑动窗口的题目再回来更新。
滑动窗口在实际生产中的典型应用,就是计算机网络中TCP协议用来进行流量控制的算法:传送门

你可能感兴趣的:(数据结构与算法,算法,数据结构)