滑动窗口(Sliding Window)算法
算法题
给定一个字符串,请你找出其中不含有重复字符的 最长子串 的长度。
1 | 输入: "abcabcbb" |
解题思路:我们可以通过一个数组来记录无重复子串,比较 s 中下一个字符是否在数组中存在,如果存在就删除,否则把下一个字符存进数组 ,比较数组的长度就可获得最大无重复子串的长度
1 | arr[0];先把第一个字符存进数组,这个数组存储的是无重复子串 |
滑动窗口算法
滑动窗口经常作为一个抽象的概念来处理数组/字符串问题,它可以将嵌套的循环问题,转换为单循环问题,降低时间复杂度, 窗口代表着一组数据/字符串元素,通过开头和结尾的索引来定义窗口。例如下图中滑动窗口就是长度固定为 2 的窗口。

上面这道题也是利用了滑动窗口算法,窗口由不重复的字符组成,题目求的就是窗口的最大长度。
如何维护滑动弄窗口呢?如何找出重复的字符?下面有两种方法
- 借助数组方法
indexOf(string i)
,当一个数组内存在 i 时,就会返回这个数组的下标,否则返回-1 - 借助哈希表的 has(string i)方法,当哈希表存在 i 为真,否则为假。
首先介绍通过数组来维护滑动窗口
- 新建一个用来存储不重复元素的数组 arr,数组的长度就是活动窗口的长度
- i = 0 ,开启滑动窗口,添加数组元素,就相当于右移滑动窗口
- 当遇见重复字符的时候,删除 arr 数组中 重复字符 ,将当前字符加入 arr 数组 ,
- 比较当前数组的长度 和 上一次的 max 值并取最大
- 依次滑动,直至移动 s 的最后一个字符
1 | let lengthOfLongestSubstring = function(s) { |
通过哈希表维护滑动窗口
- 新建一个哈希 occ
- 滑动窗口的区间长度是 [ i, rk] , rk 的初试值初始值为 -1,相当于我们在字符串的左边界的左侧,还没有开始移动
- 检查是否有重复元素
rk + 1 < n && !occ.has(s.charAt(rk + 1))
,把不重复元素加入哈希表 - 遇到重复元素时,由于 set 里面存储的元素顺序会发生变化,因此无法通过下标找到重复元素,因此可以删除 s 字符串的元素,
occ.delete(s.charAt(i - 1));
,直至删除重复元素。 - 滑动窗口的长度是 rk - i + 1 ,每次循环比较当前滑动窗口长度 和 max 取最大。
1 | // 哈希表 窗口区间就是[ i, rk] |
上面是通过 set 对象来维护滑动窗口,由于 set 只会存储 key 值,无法直接找到被删除元素。但是 map 存储的是键值对,key 是我们查找的字符 ,value 是 key 的位置。
1 | var lengthOfLongestSubstring = function(s) { |