题目
给定一个含有 n 个正整数的数组和一个正整数 s ,找出该数组中满足其和 ≥ s 的长度最小的 连续 子数组,并返回其长度。如果不存在符合条件的子数组,返回 0。
示例:
输入:s = 7, nums = [2,3,1,2,4,3]
输出:2
解释:子数组 [4,3] 是该条件下的长度最小的子数组。
解题思路
利用滑动窗口求解。
先设置两个指针,都初始化为 0 ,分别记录子数组的头部和尾部,for 循环遍历数组,并控制右指针 R 的移动,内嵌 while 循环控制左指针 L 的移动,在移动过程中比较 len 和 R - L + 1 的值的大小,将较小值赋值给 len ,当 len == 1 时,可以直接返回并退出循环。
代码如下:
public static int minSubArrayLen(int[] nums, int target) {
// 空数组直接返回 0
if(nums == null) return 0;
int L = 0, R = 0;
int len = Integer.MAX_VALUE;
int num = 0;
// 以右指针 R 遍历数组
for(;R < nums.length; R++) {
// 每循环一次就将右指针指向的元素加到 num 中,再和 target 比较,
// 若是小于 target ,则直接进入下一循环
num += nums[R];
if(num < target) continue;
// 否则比较 len 和 R - L + 1 的大小,将较小的值赋值给 len
else {
len = R - L + 1 < len ? R - L + 1 : len;
// 内嵌一个循环,当 num >= target 时,num 减去左指针 L 指向的元素,同时左指针向右移动一位
while(num >= target) {
num -= nums[L];
L++;
// 如果 len 的值大于 R - L + 1 的值,则将 R - L + 1 的值赋值给 len ,否则不操作
// 注意这里不能直接 len--,否则在特殊情况下会出现 len = 0 的错误结果
if(num >= target && len > R - L + 1) len = R - L + 1;
}
}
// len == 1 可以直接返回
if(len == 1) return len;
}
// len 的值没有变化说明没有匹配的长度最小数组,返回 0
if(len == Integer.MAX_VALUE) return 0;
return len;
}
优化思路
我们可以尝试逆向思维,前面我们是将数组元素相加再和 target 进行比较,现在我们反过来,直接用 target 减去数组中的元素,然后和 0 比较大小。
R++ 和 L++ 放到 nums[ ] 里,使用条件表达式代替判断语句,使得代码更加简洁。
代码如下:
public static int minSubArrayLen(int[] nums, int target) {
int L = 0, R = 0, min = Integer.MAX_VALUE;
while(R < nums.length) {
target -= nums[R++];
while(target <= 0) {
min = R - L < min ? R - L : min;
target += nums[L++];
}
}
return min == Integer.MAX_VALUE ? 0 : min;
}