一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第14天,点击查看活动详情。
题目描述
给定一个含有 n 个正整数的数组和一个正整数 target 。
找出该数组中满足其和 ≥ target 的长度最小的 连续子数组 [numsl, numsl+1, ..., numsr-1, numsr] ,并返回其长度。如果不存在符合条件的子数组,返回 0 。
示例 1:
输入:target = 7, nums = [2,3,1,2,4,3]
输出:2
解释:子数组 [4,3] 是该条件下的长度最小的子数组。
示例 2:
输入:target = 4, nums = [1,4,4]
输出:1
示例 3:
输入:target = 11, nums = [1,1,1,1,1,1,1,1]
输出:0
提示:
- 1 <= target <= 109
- 1 <= nums.length <= 105
- 1 <= nums[i] <= 105
思路
我们先想直接的暴力方法,遍历数组,把当前元素作为字数组的开始元素i,然后向后找,直到找到结束元素j,使得
>= target。那么,nums[i]~nums[j]就是一个符合题意的子数组,我们的目标就是求解这样所有符合条件的字数组的最小长度,如果不存在,就返回0。这样的暴力方法,时间复杂度是O(n^2),可能是超时。
仔细想一下上面的过程,我们发现了一些重复的计算,比如数据中的示例1,我们以第0个元素开头,第3个元素结束的子数组2,3,1,2和为8,是符合要求的;然后我们以1个元素开头,第4个元素结束的子数组3,1,2,4和为10,也是符合要求的,但是我们发现,相同的是3,1,2这部分,我们求和了2遍。
求解符合条件的子数组的过程,我们可以用图表示出来:
看了这个图,应该比较容易发现,这题可以使用滑动窗口:定义2个指针start和end,初始都指向nums[0],然后如果当前窗口和sum < target,那么就让end往右滑;如果sum >= target,下一步就让start往右滑,这样我们可以在O(n)的时间复杂度下,求的所有的符合条件的子数组。
注意,初始化的时候,我们定义符合条件的子数组为Integer的最大值,然后不断去减小,但是要注意不存在符合条件子数组的情况,所以最后返回前,还要打一个补丁,如果还是Integer的最大值,要将ans改为0。
Java版本代码
class Solution {
public int minSubArrayLen(int target, int[] nums) {
int ans = Integer.MAX_VALUE;
int start = 0, end = 0;
int sum = 0;
int len = nums.length;
while (end < len) {
sum += nums[end];
while (sum >= target && start <= end) {
ans = Integer.min(ans, end-start+1);
sum -= nums[start];
start++;
}
end++;
}
// ans还是初始值,证明没有符合条件的子数组
if (ans == Integer.MAX_VALUE) {
ans = 0;
}
return ans;
}
}