209.长度最小的子数组

给定一个含有 n 个正整数的数组和一个正整数 target

找出该数组中满足其总和大于等于 target 的长度最小的 连续

子数组

[numsl, numsl+1, ..., numsr-1, numsr] ,并返回其长度如果不存在符合条件的子数组,返回 0

示例 1:

1
2
3
输入:target = 7, nums = [2,3,1,2,4,3]
输出:2
解释:子数组 [4,3] 是该条件下的长度最小的子数组。

示例 2:

1
2
输入:target = 4, nums = [1,4,4]
输出:1

示例 3:

1
2
输入:target = 11, nums = [1,1,1,1,1,1,1,1]
输出:0

Solution

bp显然会超时,必须考虑利用滑动窗口来解决

所谓滑动窗口,就是不断的调节子序列的起始位置和终止位置,从而得出我们要想的结果

在暴力解法中,是一个for循环滑动窗口的起始位置,一个for循环为滑动窗口的终止位置,用两个for循环 完成了一个不断搜索区间的过程。那么滑动窗口如何用一个for循环来完成这个操作呢?

首先要思考如果用一个for循环,那么应该表示 滑动窗口的起始位置,还是终止位置。

如果只用一个for循环来表示 滑动窗口的起始位置,那么如何遍历剩下的终止位置?这样和bp算法有什么区别?

所以只用一个for循环,那么这个循环的索引,一定是表示 滑动窗口的终止位置

那么问题来了, 滑动窗口的起始位置如何移动呢?这里还是以题目中的示例来举例,s=7, 数组是[2,3,1,2,4,3],来看一下查找的过程:

209.长度最小的子数组

最后找到 4,3 是最短距离。其实从动画中可以发现滑动窗口也可以理解为双指针法的一种!只不过这种解法更像是一个窗口的移动,所以叫做滑动窗口更适合一些。


在本题中实现滑动窗口,主要确定如下三点:

  • 窗口内是什么?
  • 如何移动窗口的起始位置?
  • 如何移动窗口的结束位置?

窗口就是 满足其和 ≥ s 的长度最小的 连续 子数组。

窗口的起始位置如何移动:如果当前窗口的值大于等于s了,窗口就要向前移动了(也就是该缩小了)。

窗口的结束位置如何移动:窗口的结束位置就是遍历数组的指针,也就是for循环里的索引。

根据以上思想,代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
class Solution {
// 滑动窗口方法求解最短连续子数组的长度
public int minSubArrayLen(int s, int[] nums) {
int left = 0; // 定义滑动窗口的左边界
int sum = 0; // 当前窗口内元素的总和
int result = Integer.MAX_VALUE; // 用于保存最小长度,初始化为最大整数,以便于后续比较

// 遍历数组,右边界right逐渐向右移动,扩大窗口
for (int right = 0; right < nums.length; right++) {
sum += nums[right]; // 累加当前右边界元素到总和中

// 当当前窗口的总和大于等于目标值s时,尝试缩小窗口
while (sum >= s) {
// 更新最小长度
result = Math.min(result, right - left + 1);
// 缩小窗口:移动左边界,并从总和中减去移出窗口的元素
sum -= nums[left++];
}
}
// 如果result仍为初始值,则表示没有找到符合条件的子数组,返回0;否则返回result
return result == Integer.MAX_VALUE ? 0 : result;
}
}