[209. 长度最小的子数组]
难度 中等
给定一个含有 n 个正整数的数组和一个正整数 **s ,找出该数组中满足其和 ≥ s 的长度最小的连续子数组,并返回其长度。**如果不存在符合条件的连续子数组,返回 0。
示例:
输入: s = 7, nums = [2,3,1,2,4,3]
输出: 2
解释: 子数组 [4,3] 是该条件下的长度最小的连续子数组。
进阶:
如果你已经完成了O(n) 时间复杂度的解法, 请尝试 O(n log n) 时间复杂度的解法。
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/minimum-size-subarray-sum
解法一:枚举法
初始化子数组的最小长度为无穷大,枚举数组 nums 中的每个下标作为子数组的开始下标,对于每个开始下标 i,需要找到大于或等于 i 的最小下标 j,使得从 nums[i] 到 nums[j] 的元素和大于或等于 s,并更新子数组的最小长度(此时子数组的长度是 j−i+1)。
class Solution {
public int minSubArrayLen(int s, int[] nums) {
int len = nums.length;
int ans = Integer.MAX_VALUE;
for(int i = 0; i < len; i++) {
// 如果 nums[i] >= s, 则返回 1
if(nums[i] >= s) {
return 1;
}
int sum = 0;
//从 i 位置向后枚举
for(int j = i; j < len; j++) {
sum += nums[j];
// 如果 sum >= s, 更新 ans
if(sum >= s) {
ans = Math.min(ans, j - i + 1);
break;
}
}
}
return ans == Integer.MAX_VALUE ? 0 : ans;
}
}
解法二:前缀和 + 二分查找
class Solution {
public int minSubArrayLen(int s, int[] nums) {
int len = nums.length;
if (len == 0) {
return 0;
}
int ans = Integer.MAX_VALUE;
//计算前缀和
int[] sums = new int[len + 1];
for (int i = 1; i <= len; i++) {
sums[i] = sums[i - 1] + nums[i - 1];
}
for (int i = 1; i <= len; i++) {
// target = s + sums[i - 1] 表示查找到 target 的位置 bound 后,
//sums[bound] - sums[i - 1] 与 s 最接近(即 从下标[0,...,bound] 的和与 s 最接近)
int target = s + sums[i - 1];
// 如果 bound < 0 ,则返回的插入位置为 -bound - 1
int bound = Arrays.binarySearch(sums, target);
if (bound < 0) {
bound = -bound - 1;
}
if (bound <= len) {
ans = Math.min(ans, bound - (i - 1));
}
}
return ans == Integer.MAX_VALUE ? 0 : ans;
}
}
解法三:双指针
采用双指针来维护一个窗口,开始时 begin = 0, end = 0,然后不断向窗口内添加值来扩大窗口(end++)直到窗口内元素的和 ≥ s ,然后尝试从左边缩小窗口(begin++),判断缩小后的窗口是否 ≥ s ,并更新 ans 的值。
class Solution {
public int minSubArrayLen(int s, int[] nums) {
int ans = Integer.MAX_VALUE, sum = 0;
int begin = 0, end = 0;
while(end < nums.length) {
sum += nums[end++];
//如果 窗口内元素的和 ≥ s,则尝试从左边缩小窗口
while(sum >= s) {
ans = Math.min(ans, end - begin);
sum -= nums[begin++];
}
}
return ans == Integer.MAX_VALUE? 0 : ans;
}
}