力扣:209.长度最小的子数组

1,935 阅读1分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 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 <= 109
  • 1 <= nums.length <= 105
  • 1 <= 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;
    }
};