滑动窗口算法
滑动窗口算法又叫'同向双指针算法',核心在于维护一个窗口,这个窗口的左右边界可以根据需要动态调整。窗口的大小通常取决于当前窗口内的元素是否满足某种条件。通过移动窗口的左右边界,可以在一次遍历中找到满足条件的子数组或子串。
例题讲解
2.1. 长度最小的子数组
题目要求找出长度最小的子数组,并且子数组的和大于等于目标值。暴力枚举策略,就是找出所有和大于等于目标值的子数组,我们需要找出子数组的左右区间,还要遍历一遍子数组求和,那么时间复杂度为 O(n^3)。
接下来对暴力枚举进行优化。题目中给出了条件,数组里的元素都是正整数。我们可以定义两个指针 left 和 right,让 right 向右移动,left 和 right 就可以构成一个子数组。当子数组的和大于等于目标值时就固定,让 left 指针也向右移动。因为数组是具有单调性的,两个指针不用回退,就可以省去很多不必要的枚举,让子数组里的元素不停进出,维护更新子数组的和。我们操作只有 right 和 left 的移动,那么时间复杂度为 O(n)。
代码实现:
class Solution {
public int minSubArrayLen(int target, int[] nums) {
int sum = 0, len = Integer.MAX_VALUE;
for (int left = 0, right = 0; right < nums.length; right++) {
sum += nums[right]; // 进窗口
while (sum >= target) {
len = Math.min(len, right - left + 1);
sum -= nums[left++];
}
}
return len == Integer.MAX_VALUE ? 0 : len; // 如果给定的数组和小于目标值,直接返回 0
}
}
2.2. 无重复字符的最长子串
第一种解法,使用暴力枚举,即找出所有不含重复字符的子串,再比较长度大小。那我们如何知道字符重复出现呢?我们在这里可以使用哈希表,同样是定义两个指针 left 和 right,让 right 把遍历过的字符扔进哈希表里面,此时我们就需要遍历两遍子串,时间复杂度为 O(n^2)。
当 right 遇到重复的字符时,left 向右移动。按照暴力枚举的策略,right 还需要回来,再次向右移动,这是没有必要了。因为 left 与 right 之间还有与 right 指向相同的字符,那么 left 指针此时就可以直接跳过了。由于两个指针也是同向移动的,就可以利用滑动窗口来解决。
滑动窗口的解决过程就是'进窗口→判断→出窗口→更新结果'。'进窗口'就是将字符扔进哈希表中;'判断'的过程就是看看哈希表中是否存在重复的字符;'出窗口'就是从哈希表中删除重复的字符。
完整代码实现:
class Solution {
{
[] str = s.toCharArray();
[] hash = [];
, right = , n = s.length();
;
(right < n) {
hash[str[right]]++;
(hash[str[right]] > ) {
hash[str[left++]]--;
}
ret = Math.max(ret, right - left + );
right++;
}
ret;
}
}


