滑动窗口经典题(双指针)
题目
给定一个含有 n
个正整数的数组和一个正整数 target
。找出该数组中满足其总和大于等于 target
的长度最小的 子数组 [numsl, numsl+1, ..., numsr-1, numsr]
,并返回其长度。如果不存在符合条件的子数组,返回 0
。
核心思想:
滑动窗口用于解决子数组或子区间问题,尤其适合处理满足特定条件的最优解,例如最小长度、最大和等。
-
定义窗口范围:通常用两个指针
left
和right
表示窗口的左右边界。 -
滑动规则:
a.扩大窗口:通过移动 right
增大窗口,以满足问题条件。
b.缩小窗口:在满足条件的前提下,通过移动 left
尽量缩小窗口。
public int minSubArrayLen(int target, int[] nums) {
int left = 0, right = 0;
int sum = nums[0];
if (sum >= target) return 1; // 特判:第一个元素已经满足条件
int min = Integer.MAX_VALUE;
while (right + 1 < nums.length) {
right++;
sum += nums[right];
// 收缩窗口
while (sum >= target) {
min = Math.min(min, right - left + 1);
if (min == 1) return 1; // 提前返回,最小长度为1
sum -= nums[left];
left++;
}
}
return min == Integer.MAX_VALUE ? 0 : min; // 无法找到满足条件的子数组
}
-
时间复杂度:O(n)
- 每个元素最多被访问两次(一次通过
right
,一次通过left
),因此复杂度为线性。
- 每个元素最多被访问两次(一次通过
-
空间复杂度:O(1)
- 只使用了几个额外变量,无额外数组存储需求。
滑动窗口总结
滑动窗口适用场景
-
固定窗口大小:如最大或最小的子数组和、最大连续 1 的个数等。
-
可变窗口大小:如本题寻找最小长度的子数组,或寻找和为某值的最长子数组。
扩展问题
-
最大长度问题:例如,求和小于等于某值的最长子数组长度。
-
固定和问题:子数组和恰好等于某值的最长或最短子数组长度。