给定一个含有 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
示例 3:
输入:target = 11, nums = [1,1,1,1,1,1,1,1]
输出:0
实现代码:
暴力解法
class Solution {
public:
int minSubArrayLen(int target, vector<int>& nums)
{
int length = INT_MAX;//记录最小的子串长度,初始为int最大值
for (int i = 0; i < nums.size(); i++)
{
int sum = 0;//记录子串元素的和,用于比较
for (int j = i; j < nums.size(); j++)
{
sum += nums[j];
if (sum >= target)
{//子串总和超过目标值,进行更新
int len = j - i + 1;//临时长度用于比较
length = length < len ? length:len;
break;//找到符合条件的后,退出内层循环,继续找下一个符合条件的子串进行比较
}
}
}
return length == INT_MAX ? 0 : length;//判断是否没有一个字串符合要求
}
};
此方法会超出时间限制。
- 时间复杂度:O(n^2)
- 空间复杂度:O(1)
滑动窗口法
class Solution {
public:
int minSubArrayLen(int target, vector<int>& nums)
{
int length=INT_MAX;//字串长度.初始为最大值
int i=0;//起始指针
int sum=0;//总和
for(int j=0;j<nums.size();j++)
{
sum+=nums[j];
while(sum>=target)
{
int len=j-i+1;//用于比较的临时长度
length=length<len?length:len;
sum-=nums[i++];//此方法最重要的地方,将起始元素出窗口,也就是sum减去起始元素后,指针向后移动
}
}
return length==INT_MAX?0:length;//判断是否没有符合条件的子串,返回0
}
};
利用双指针,与暴力法不同的是,此方法是先找到满足条件的字串后再移动,而暴力法是一直利用双重循环移动指针,再不断找到满足条件的进行判断,可见此方法要比暴力法好很多。需要注意的是循环内的while语句,起始元素出窗口的移动和sum的改变。至于为什么用while而不是if,因为如果子串前面的数很小,后面的数很大,后面的数才是影响字串sum大小的关键,用if只出窗口一个元素的话,显然是忽略了起始元素出窗口后,子串sum任然大于等于目标值的情况,从而得不到最小子串长度。
- 时间复杂度:O(n)
- 空间复杂度:O(1)
每个元素入窗口和出窗口仅仅被操作两次,所以时间复杂度为2n也就是O(n).