暴力法
两个for循环,然后不断的寻找符合条件的子序列,时间复杂度很明显是O(n^2) 。
时间复杂度:O(n^2)
空间复杂度:O(1)
class Solution {
public:
int minSubArrayLen(int target, vector<int>& nums) {
int result=INT32_MAX;//最终结果。要返回长度最小长度,起始设置为最大,等待更新
int sum; //用来计算子序列的数值之和
int sublen;//用来记录子序列的长度
int size=nums.size();
for(int ii=0;ii<size;ii++){// 设置子序列起点为ii
sum=0;
for(int jj=ii;jj<size;jj++){ // 设置子序列终止位置为jj
sum+=nums[jj];
if(sum>=target){// 一旦发现子序列和超过了target,更新result
sublen=jj-ii+1;// 计算子序列的长度
result=result<sublen ? result:sublen;
break;// 因为我们是找符合条件最短的子序列,所以一旦符合条件就break
}
}
}
// 如果result没有被赋值的话,就返回0,说明没有符合条件的子序列
return result==INT32_MAX? 0:result;
}
};
滑动窗口
所谓滑动窗口,「就是不断的调节子序列的起始位置和终止位置,从而得出我们要想的结果」。
其实从动画中可以发现滑动窗口也可以理解为双指针法的一种!只不过这种解法更像是一个窗口的移动,所以叫做滑动窗口更适合一些。
在本题中实现滑动窗口,主要确定如下三点:
窗口内是什么?
如何移动窗口的起始位置?
如何移动窗口的结束位置?
窗口就是 满足其和 ≥ s 的长度最小的 连续 子数组。
窗口的起始位置如何移动:如果当前窗口的值大于s了,窗口就要向前移动了(也就是该缩小了)。
窗口的结束位置如何移动:窗口的结束位置就是遍历数组的指针,窗口的起始位置设置为数组的起始位置就可以了。
解题的关键在于 窗口的起始位置如何移动,如图所示:
可以发现「滑动窗口的精妙之处在于根据当前子序列和大小的情况,不断调节子序列的起始位置。从而将O(n^2)的暴力解法降为O(n)。
class Solution{
public:
int minSubArrayLen(int target,vector<int>& nums){
int result=INT32_MAX;
int sum=0; // 滑动窗口数值之和
int sublen=0;// 滑动窗口的长度
int ileft=0;//滑动窗口起始位置
int size=nums.size();
//滑动窗口结束位置其实就是遍历数组的指针
for(int iright=0;iright<size;iright++){
sum+=nums[iright];
while(sum>=target){
sublen=iright-ileft+1;
result=result<sublen? result:sublen;
//双指针暴力解法中是跳出第二层循环到第一层循环刷新sum=0,然后移动子序列起点,在进入第二层循环重新计算sum
//此处滑动窗口,直接改变滑动窗口起始位置,不必将sum归零重新计算,只是把起始位置值删去即可
sum-=nums[ileft++];// 这里体现出滑动窗口的精髓之处,不断变更i(子序列的起始位置)
}
}
return result==INT32_MAX? 0:result;
}
};