给定一个含有 n 个正整数的数组和一个正整数 s ,找出该数组中满足其和 ≥ s 的长度最小的连续子数组。如果不存在符合条件的连续子数组,返回 0。
示例:
输入: s = 7, nums = [2,3,1,2,4,3]
输出: 2
解释: 子数组 [4,3] 是该条件下的长度最小的连续子数组。
进阶:
如果你已经完成了O(n) 时间复杂度的解法, 请尝试 O(n log n) 时间复杂度的解法。
方法一:二分,时间复杂度O(nlog(n))
我做这道题的二分法有点绕,主要是边界问题;
首先我写的二分查找是返回大于等于目标数的元素索引,如果目标大于所有值返回-1.
原数组nums = [2,3,1,2,4,3]
前i项和数组sums = [0,2,5,6,8,12,15]
如果从nums的第0项开始找,即找[2,3,1,2] = 8 > 7,也就是nums的第0,1,2,3项,
那么怎么用sums的数组表示呢,nums从第0项开始对应sums从第一项开始找,
即sums[4] - sums[0] = 8 - 0 = 8,这里sums的第四项就是用二分查找到的,
若在target大于所有sums中的数字,二分会返回-1,即没有满足的子数组
public class Solution {
public int minSubArrayLen(int s, int[] nums) {
if(nums.length == 0){
return 0;
}
int result = Integer.MAX_VALUE;
int[] sums = new int[nums.length + 1];
for(int i = 1; i < sums.length; i++){
sums[i] = sums[i - 1] + nums[i - 1];
}
for(int i = 1; i < sums.length; i++){
int tmp = lowerBound(sums, 0, sums.length, s + sums[i - 1]);
if(tmp != -1){
result = Math.min(result, tmp - i + 1);
}
}
if(result == Integer.MAX_VALUE){
return 0;
}
return result;
}
private int lowerBound(int[] nums, int L, int R, int target){
while(L < R){
int mid = L + (R - L) / 2;
if(target <= nums[mid]){
R = mid;
}
else{
L = mid + 1;
}
}
if(L == nums.length) return -1;
return L;
}
方法二:双指针,时间复杂度O(n)
左指针指向首元素,右指针不断右移,知道左右指针之间的数和满足要求,记录长度,左指针不断右移,更新长度,直到和不满足要求,再将右指针右移,从而遍历所有子数组。
public class Solution {
public int minSubArrayLen(int s, int[] nums) {
int n = nums.length;
int result = Integer.MAX_VALUE;
int L = 0, sum = 0;
for(int i = 0; i < n; i++) {
sum += nums[i];
while(sum >= s){
result = Math.min(result, i + 1 - L);
sum -= nums[L++];
}
}
return result != Integer.MAX_VALUE ? result : 0;
}
}