滑动窗口_长度最小的子数组_C++

1.题目解析


leedcode题目链接:https://leetcode.cn/problems/2VG8Kg/

给定一个含有 n 个正整数的数组和一个正整数 target 。

找出该数组中满足其和 ≥ targe 的长度最小的 连续子数组 [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

通过本题的学习,我们要搞懂五个问题:

1)什么是滑动窗口?
2)什么时候用滑动窗口?
3)怎么用滑动窗口?
4)滑动窗口的正确性(滑动窗口为什么是对的?)
5)滑动窗口的时间复杂度。


2.算法原理


2.1暴力解法:暴力枚举出所有的子数组的和


1.纯暴力枚举

暴力枚举出所有子数组,并求它们的和,找出符合要求的子数组,然后再找出最优的子数组。

[2, 3, 1, 2, 4, 3]为例,我们先定义一个枚举的区间,比如是下标0到3之间的数[2, 3, 1, 2],对这个区间求和,需要遍历一遍数组,是O(n)。而遍历枚举区间,又需要用到两层for循环,是一个O(n^2)的时间复杂度。综上,纯暴力枚举的时间复杂度是O(n^3)

2.优化暴力解法

还是以[2, 3, 1, 2, 4, 3]为例,target为7:

在这里插入图片描述

我们先定义两个指针leftright,它们开始时都指向0这个位置。再定义一个变量sum,用来记录子数组中所有元素的和。

1)最开始,子数组中只有元素2,我们让sum = 2,然后让right++right此时指向的位置值是3。
2)此时子数组为[2, 3]sum更新为2+3=5,然后再让right++,依次类推。
3)当right已经指向最后一个位置时,再更新两个指针的位置,让它们再从1的位置重复上述操作。

之前,纯暴力解法时,为了计算子数组的和,需要再遍历一遍子数组,时间复杂度是一个O(n),加上两层for循环的复杂度,时间复杂度为O(n^3)。而这种双指针的优化写法,省去了遍历子数组求和的操作,使得时间复杂度直接降低了一维,只有两个指针不断遍历的时间复杂度,为O(n^2)


2.2利用规律,使用“同向双指针”来解题


注意题中的一个条件:数组中所有的数,都是大于等于0的。所以如果选出一个子数组,它已经满足条件了,那么再向其中增加元素,数组的总和一定是增大或者不变的,那就没必要再向子数组中增加元素了。我们能否根据这个性质,来做一些优化?

1.以2.1中的例子为例:

在这里插入图片描述

如图,我们按照优化的暴力算法,已经让right指向了下标为3的位置,然后更新sum,发现这是一个满足条件的子数组。那么根据最开始所说的规律,如果此时我们再让right++,再向子数组中增加新的元素,它的结果一定是满足条件的,但是长度会变长,最后也一定不是我们想要的长度最小的子数组。

所以我们可以把right向后移动所产生的一串数组大胆舍弃。为了达到这个效果,我们可以固定right不动,然后移动left,让left++,得到新的子数组,更新sum,再判断这个新的子数组sum是否大于等于target。当满足条件时,我们更新长度len,再让left++,进行下一轮判断;当不满足时,我们让right++,再进行下一轮判断。以此类推。

我们不难发现,指针leftright,是同向移动的,我们称之为“同向双指针”。而且这样的一对同向双指针,共同维护了一小块区间内的数据,且这一小块区间不断地向后滑动,我们形象的称其为:“滑动窗口”。

2.滑动窗口如何用?

1)先定义两个指针leftright,让它们都初始化为0。

2)构建窗口,进窗口。

3)根据判断条件,判断是否要出窗口。

其中2和3步一直循环。

什么时候更新结果呢?这个要就提论题。有的题是在进窗口的时候更新结果,有的题是在判断的时候更新结果。就本题而言,我们要先判断,然后要更新结果,再出窗口。

3.滑动窗口的正确性

虽然,我们没有将所有的子数组都枚举出来,进行判断。但是,我们考虑了所有的情况,只是把很多不合适的数组提前舍弃了,规避了很多没有必要的枚举行为,所以是正确的。

4.时间复杂度分析

在整个过程中,只有leftright在移动。最坏的情况下,right移动到最后一个数,left也移动到最后一个数,时间复杂度是n+n,一共才O(n)


3.代码编写


class Solution {
public:
    int minSubArrayLen(int target, vector<int>& nums) 
    {
        int left = 0, right = 0;
        int len = INT_MAX;            // 长度,初始化为int的最大值
        int sum = nums[0];

        while(left <= right)
        {
            if(sum >= target) 
            {
                len = min(len, right - left + 1);         // 更新结果
                sum -= nums[left];
                left++;
            } 
            else
            {
                right++;
                if(right >= nums.size()) break; // 防止越界
                sum += nums[right];
            }
        }

        if(left == 0 && right >= nums.size()) return 0;	// 如果if中的条件满足,说明没有合适的子数组满足条件,返回长度0
        return len;
    }
};

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

-指短琴长-

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值