持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第7天,点击查看活动详情
力扣——209.长度最小的子数组
209. 长度最小的子数组
问题解析
给定一个含有 n 个正整数的数组和一个正整数 target 。
找出该数组中满足其和 ≥ target 的长度最小的 连续子数组 [numsl, numsl+1, ..., numsr-1, numsr] ,并返回其长度。如果不存在符合条件的子数组,返回 0 。
示例 1:
输入:target = 7, nums = [2,3,1,2,4,3]
输出:2
解释:子数组 [4,3] 是该条件下的长度最小的子数组。
示例 2:
输入:target = 4, nums = [1,4,4]
输出:1
提示:
1 <= target <= 1091 <= nums.length <= 1051 <= nums[i] <= 105
问题解析
二分+前缀和
由于数组中所有的元素都为正数,那么,如果有一个长度为mid的数组能满足它的和大于等于target,那么也一定有长度为mid+1的数组的和能满足大于等于target。
满足二分的单调性,所以这题我们可以用二分来写。
二分答案枚举可能的数组长度,每次用枚举的长度mid来看是否有长度为mid的子数组和大于等于target,如果有,我们去左半边继续找更小的长度;如果没有,我们去右半边找更大的长度。如果最后也不满足条件,我们返回0。
至于算子数组和的操作,我们可以用前缀和来化简。
时间复杂度:O(nlogn);
空间复杂度:O(n);
class Solution {
public:
int f[100050],n;
bool check(int mid,int target)
{
for(int i=0;i<=n-mid;i++)
{
if(f[mid+i]-f[i]>=target)return true;
}
return false;
}
int minSubArrayLen(int target, vector<int>& nums) {
n=nums.size();
for(int i=1;i<=n;i++)
{
f[i]=f[i-1]+nums[i-1];
}
int l=1,r=n;
while(l<r)
{
int mid=(l+r)/2;
if(check(mid,target))r=mid;
else l=mid+1;
}
if(!check(l,target))return 0;
return l;
}
};
滑动窗口
双指针当滑动窗口,当滑动窗口内和小于target时,右指针移动;当和大于等于target时,记录当前的长度,然后左指针移动。在此过程中维护最小值。
时间复杂度:O(n);
空间复杂度:O(1);
class Solution {
public:
int minSubArrayLen(int target, vector<int>& nums) {
int mn=1e9,n=nums.size(),l=0,r=0,sum=0;
while(r<=n)
{
if(sum>=target)
{
mn=min(mn,r-l);
sum-=nums[l];
l++;
}
else
{
if(r>=n)break;
sum+=nums[r];
r++;
}
}
if(mn==1e9)return 0;
return mn;
}
};