题目(难度:中等):
给定一个含有 n 个正整数的数组和一个正整数 s ,找出该数组中满足其和 ≥ s 的长度最小的连续子数组。如果不存在符合条件的连续子数组,返回 0。
示例:
输入:s = 7, nums = [2,3,1,2,4,3]
输出: 2 解释: 子数组[4,3]
是该条件下的长度最小的连续子数组。
解题:
利用滑动窗口,设置左边界变量l,计算 l 到 i下标之间的数组元素之和与s比较,若>=s,则记录min值并继续前移;若<s,则使 l 不动,循环继续前移。需要考虑的边界问题有:
(1)若单个数组元素>=s,则return 1; (可能出现在数组的开头,或最后或任意位置)
(2)如s=7,nums = [-6,1,6],若i=2,此时已结束循环,此时须让 l 继续前移,直至 l==i;
代码实现:
public int minSubArrayLen(int s, int[] nums) {
if (nums == null || nums.length < 1) return 0;
else if (nums.length == 1) {
if (nums[0] >= s) return 1;
else return 0;
}
int min = 0, l = 0, i = 1;
if (nums[l] >= s) return 1;
while (i < nums.length) {
if (nums[i] >= s) {
return 1;
}
int sum = 0;
for (int j = l; j <= i; j++) {
sum += nums[j];
}
if (sum >= s) {
if (min == 0) min = i - l + 1;
else min = (i - l + 1) > min ? min : (i - l + 1);
l++;
i = l + 1;
} else {
if (i == nums.length - 1) {
l++;
if (l == i) return min;
} else i++;
continue;
}
}
return min;
}
缺点:时间复杂度O(n2),当数据量巨大时,sum求和所占时间很长,耗时高。
改进:
利用左右两个指针变量,起始位置l=0,r= -1;(以r的位置求和,以l的位置相减,确保r遍历每一个数组元素)
(1)若r+1<nums.length和sum<s,则r++,sum+=nums[r];
(2)若r到达数组右端或者sum>=s,则先将sum -=nums[l];再将l++;
(3)求出满足sum>=s的min值(下标相减即可)
代码实现:
public int minSubArrayLen(int s, int[] nums) {
if (nums == null || nums.length < 1) return 0;
int l = 0, r = -1;
int min = 0, sum = 0;
while (l < nums.length) {
if (r + 1 < nums.length && sum < s) {
r++;
sum += nums[r];
} else { //r到头或者sum >= s
sum -= nums[l];
l++;
}
if (sum >= s) {
if(min==0) min=r-l+1;
min = min >(r - l + 1) ? (r - l + 1) : min;
}
}
return min;
}
测试用例:
算法分析:
算法时间复杂度O(n),额外空间复杂度O(1)