209.长度最小的子数组
描述
给定一个含有 n **个正整数的数组和一个正整数 target **。
找出该数组中满足其总和大于等于 ****target ****的长度最小的 连续子数组 [numsl, numsl+1, ..., numsr-1, numsr] ,并返回其长度 。 如果不存在符合条件的子数组,返回 0 。
分析
- 解法1:使用双重循环查找。找出每个以nums[i]开头且总和大于等于target的数组,返回长度最小的数组长度。时间复杂度为O(N^2)
- 解法2:使用滑动窗口算法。由left和right来维护窗口的长度
解法(滑动窗口算法)
- 初始化left=0,sum=0,minLength=Integer.MAX_VALUE;
- 使用for循环遍历滑动右窗口i,循环逻辑如下:
- sum+=nums[i]。
- 若sum<target, i继续右滑,left不变
- 若sum>=target,循环(while(sum >= target)),sum-=nums[left],移动left, 计算当前窗口的长度并和minLength比较,保存最小的。
时间复杂度:O(N)
提问:存在两次循环时间复杂度为什么还是O(N)? 解答:分析时间复杂度不能只看循环。看看整个过程left和right两个变量的执行的操作总数都是n ,n+n=2n,所以依旧是O(N)级别的。
代码
public int minSubArrayLen(int target, int[] nums) {
int left = 0;
int curSum = 0;
int minLength = Integer.MAX_VALUE;
//遍历每个右边界,找到每个右边界对应之下的最小左边界,
for (int i = 0; i < nums.length; i++) {
curSum += nums[i];
if (curSum < target) {
continue;
} else {
while (curSum >= target) {
minLength = Math.min(minLength, i - left + 1);
curSum -= nums[left];
left++;
}
}
}
if (minLength == Integer.MAX_VALUE) {
return 0;
}
return minLength;
}
总结
滑动窗口算法巧在循环的每一步,right在向右滑动之后,根据上一步记录的left来进行选择性滑动,从而计算得到left每滑动一步后的窗口大小,并把最小值记录下来。