引入:滑动窗口
首先,这是滑动窗口的第一道题,所以简短的说一下滑动窗口的思路:
当我们题目要求找一个满足要求的区间的时候,且这个区间的left和right指针,都只需要同向移动的时候,就可以使用滑动窗口,这个区间可以变长,变短,但是双指针一定是同向移动即可!
所以滑动窗口的题目一般分别以下3步:
窗口定义:用
left
和right
指针表示当前窗口的左右边界。移动规则:
进窗口(
right++
):当满足条件时,扩大窗口以包含新元素。出窗口(
left++
):当不满足条件时,收缩窗口以排除左侧元素。由1,2可知,left和right都是同向移动(一般是向右)
统计结果:在移动过程中,根据题目要求记录最优解(如最长/最短子数组)。
注:有时候是出窗口后再判断更新,有时候是进窗口就可以更新,视题目而定!
一:题目
解释:找一个最短的连续子数组的和 >=taget
二:算法
①:暴力
暴力解法的时间复杂度:N^3
a:枚举所有可能的子数组:一个长度为 n 的数组有 O(n^2) 个子数组(起点有 n 种选择,终点有 n 种选择,但实际是 n(n+1)/2,即 O(n^2))。
b:计算每个子数组的和:对于每个子数组,需要遍历其所有元素求和。最坏情况下(子数组长度为 n),求和的时间复杂度是 O(n)。
c:所以:总时间复杂度为 O(n) × O(n) × O(n) = O(n^3)。
暴力能优化的点:
讲暴力就是因为要对其进行优化,而优化过后就会发现其能用滑动窗口的思想来解决!
数组:[2,3,1,2,4,3]
当left 为2 right为2的时候 此时子数组和为8,如果按照暴力,此时我们的left会往走一个,然后right再回到left的位置向后继续遍历
优化1:但是其实我们right不用再动,只用left++即可,此时若是仍满足要求,则更新长度即可,反之不满足,则right++即可!
优化2:当left为更新为3的时候 此时我们right是指向2的 此时right可以不先回退! 先更新得知和为6,若此时的和仍大于等于t(7) 则更新区间长度,然后left直接再++ !; 反之小于t则right++
所以,综上所述,次此题是一个双指针同向移动的题目,所以用滑动窗口来解决!
②:滑动窗口
采取和滑动窗口之后,我们的双指针最多只用遍历数组一遍,所以时间复杂度为O(N)
三:代码
class Solution {
public:int minSubArrayLen(int target, vector<int>& nums) {int left = 0,right=0,len = INT_MAX,sum=0;for(;right<nums.size();right++){sum+=nums[right];//窗口总值++while(sum>=target)//窗口符合要求{len=min(len,(right-left+1));//取区间最小值去更新lensum-=nums[left];//出窗口 减去left的值 left++;//left++}}return (len==INT_MAX?0:len);//为INT_MAX代表此题无解,则返回0}
};
易错点:
①:当窗口满足要求的时候,一定是while循环,然后出一次窗口,判断一次是否窗口还符合要求,符合则继续出窗口,直到窗口不符合要求
②:更新结果,不是窗口满足要求就更新到len,而是把当前次满足要求的结果和之前的len取较小值去更新
③:len一开始直接给个最大值,因为我们求得是区间的最小值。滑动窗口的题目,让你求最小你就给INT_MAX,让你求最大值,你初识为0,准没错!