数组
长度最小的子数组
给定一个含有 n
个正整数的数组和一个正整数 target
。
找出该数组中满足其和 ≥ target
的长度最小的 连续子数组 [numsl, numsl+1, ..., numsr-1, numsr]
,并返回其长度 。 如果不存在符合条件的子数组,返回 0
。
-
暴力解法思路
遍历数组,记录以nums[i]开头的满足条件的连续子数组的长度,不断更新该长度,直到找到最小的满足条件的连续子数组的长度。 -
代码如下:
class Solution {
public int minSubArrayLen(int target, int[] nums) {
int res = nums.length + 1;
int sum = 0;
for (int i = 0; i < nums.length; i++) {
for (int j = i; j < nums.length; j++) {
sum += nums[j];
if (sum >= target){
res = Math.min(j - i + 1,res);
break;
}
}
sum = 0;
}
if (res == nums.length + 1) return 0;
return res;
}
}
暴力解法的时间复杂度为O(n^2),空间复杂度为O(1)。
- 滑动窗口解法思路
滑动窗口:不断调节子序列的起始位置和终止位置,得到想要的结果。
在暴力解法中,是一个for循环为滑动窗口的起始位置,一个for循环为滑动窗口的终止位置,用两个for循环完成了一个不断搜索区间的过程。
那么怎样仅仅使用一个for循环来完成这个操作?
首先思考:
如果使用一个for循环,那么应该表示滑动窗口的起始位置还是终止位置呢?
如果表示起始位置,那终止位置难以表示,因此应该表示终止位置。
那么起始位置应该如何表示呢?
在遍历终止位置时,通过变量将遍历过的元素和记录下来,只要区间满足条件,就将初始位置指针右移,这样就实现了窗口的滑动。 - 代码如下:
class Solution {
public int minSubArrayLen(int target, int[] nums) {
int res = nums.length + 1;
int sum = 0; // 滑动窗口数值之和
int firstIndex = 0; // 滑动窗口起始位置
for (int i = 0; i < nums.length; i++) {
sum += nums[i];
// 这里使用while,每次更新起始位置firstIndex,并不断比较子序列是否满足条件
while (sum >= target) {
res = Math.min(res, i - firstIndex + 1);
sum -= nums[firstIndex];
firstIndex++; // 不断变更子序列的起始位置
}
}
if (res == nums.length + 1) return 0;
return res;
}
}
根据当前子序列和的大小,不断调节子序列的起止位置。
时间复杂度为O(n),空间复杂度为O(1)。