209. 长度最小的子数组
给定一个含有 n 个正整数的数组和一个正整数 s ,找出该数组中满足其和 ≥ s 的长度最小的连续子数组。如果不存在符合条件的连续子数组,返回 0。
示例:
输入: s = 7, nums = [2,3,1,2,4,3] 输出: 2 解释: 子数组 [4,3] 是该条件下的长度最小的连续子数组。
进阶:
如果你已经完成了O(n) 时间复杂度的解法, 请尝试 O(n log n) 时间复杂度的解法。
解法一:
//时间复杂度O(n), 空间复杂度O(1)
class Solution {
public:
int minSubArrayLen(int s, vector<int>& nums) {
int i = 0, j = 0;
int sum = 0, res = INT_MAX;
while(true) {
if(sum >= s) {
res = min(res, j - i);
sum -= nums[i++];
}
else if(j == nums.size()) break;
else sum += nums[j++];
}
return res == INT_MAX ? 0 : res;
}
};
解法二:
引用:官方题解长度最小的子数组
//时间复杂度O(n), 空间复杂度O(n)
int minSubArrayLen(int s, vector<int>& nums)
{
int n = nums.size();
if (n == 0)
return 0;
int ans = INT_MAX;
vector<int> sums(n + 1, 0); //size = n+1 for easier calculations
//sums[0]=0 : Meaning that it is the sum of first 0 elements
//sums[1]=A[0] : Sum of first 1 elements
//ans so on...
for (int i = 1; i <= n; i++)
sums[i] = sums[i - 1] + nums[i - 1];
for (int i = 1; i <= n; i++) {
int to_find = s + sums[i - 1];
auto bound = lower_bound(sums.begin(), sums.end(), to_find);
if (bound != sums.end()) {
ans = min(ans, static_cast<int>(bound - (sums.begin() + i - 1)));
}
}
return (ans != INT_MAX) ? ans : 0;
}
解法一:
双指针法。i、j最初都指向首元素,sum代表子序列[i, j]的局部和。
在每次循环内,如果sum >= s,就向右移动i指针,同时更新sum和res;
如果sum < s,说明序列长度不够,此时向右移动j指针,同时更新sum,直到j == nums.size()且sum < s时返回res。
解法二:
题上要求时间为O(nlogn)的解法,看到对数就想到二分法。思路如下:
双重遍历数组。i为起始,在[i, nums.size() - 1]这些位置中二分查找,找出刚好使sum >= s的位置j。遍历以每一个i为起始的所有情况,对每一个i,二分查找的时间为O(n),总的时间就是O(nlogn)。
为了方便二分查找过程中快速得到任意子序列的和,可以事先建立一个前缀和数组sums,则子序列[i, j]的和为sums[j] - sums[i - 1](认为sums[-1]为0);
二分查找的前提条件是数组有序,而前缀和数组必然是增序的(题上条件有正整数)。
2020/01/11 00:12