数组--part 4--长度最小的子数组(力扣299/904/76)

文章介绍了滑动窗口算法的基本思想,并通过LeetCode的209长度最小的子数组、904水果成篮和76最小覆盖子串这三个题目,展示了如何从暴力解法进化到双指针法优化时间复杂度。文章强调了在特定情况下使用STL(特别是哈希映射)可以简化代码和提高效率。

算法基本思想

首先对于滑动窗口,题目可以先去看看leetcode 209 进行相关的了解后,再来书写代码。

首先我们的第一想法肯定就是暴力解法:也就是采用两层循环进行遍历,寻找相关的运算并得到最后的答案。下面是暴力的解法:

int minSubArrayLen(int s, vector<int>& nums) {
   
   
        int result = INT32_MAX; // int的最大值
        int sum = 0; // 子序列的数值之和
        int subLength = 0; // 子序列的长度
        for (int i = 0; i < nums.size(); i++) {
   
    // 设置子序列起点为i
            sum = 0;
            for (int j = i; j < nums.size(); j++) {
   
    // 设置子序列终止位置为j
                sum += nums[j];
                if (sum >= s) {
   
    // 一旦发现子序列和超过了s,更新result
                    subLength = j - i + 1; // 取子序列的长度
                    result = result < subLength ? result : subLength;
                    break; // 因为我们是找符合条件最短的子序列,所以一旦符合条件就break
                }
            }
        }
        // 如果result没有被赋值的话,就返回0,说明没有符合条件的子序列
        return result == INT32_MAX ? 0 : result;
    }

而对于这个代码进行分析的话,我们会发现他的时间复杂度是O(n2),实际上对于这个量级的数据,我们这种算法实际上已经需要更新的了,很容易超时。

那有没有一种方法可以有效的减少时间复杂度呢?回忆一下之前part2部分的内容,我们实际上是需要两层for循环来遍历数组的事情,我们采用双指针法来实现得到了一层循环做到了两层循环做到的事情,故双指针法是一条道路。

双指针法的实现过程:

int function1(int target, vector<int>& nums) {
   
   
	//首先我们先可以看到这里,我们决定采用双指针法,来仅仅采用一次循环达到题目所需要的要求。
	//先定义具体的指针。sum的值实际上就是数组从begin开始加到end为止
	int begin = 0, end = 0;
	int sum = 0;
	//对于我们来说,我们第一步的想法肯定是,遍历begin然后调整end来进行控制中间数据的叠加,但是当你写下begin++开始变化的时候
	//你会发现,后面的end随之变化,我们最后写出来的代码实际上也就是跟两层for循环一样,实际上也就是和暴力算法没有区别。
	for ( begin = 0; begin < nums.size(); begin++)
	{
   
   
		//定义sum 并且变化end
		
		//for 循环从begin相加到end
	}
}

上面的function的方式,我们似乎没有逃开两层循环的限制。所以我们来分析一下,为什么开始begin不能等于0,实际上我们想要让整个函数的运行仅仅只有一层for循环,我们所需要做的事情就是,随着循环的变化,begin和end仅仅只有一个自由度的变化。所以我们需要采用end的变化,来进行操作,看不懂的可以看看代码,然后理解一下过程。

int function1(int target, vector<int>& nums) {
   
   
	//首先我们先可以看到这里,我们决定采用双指针法,来仅仅采用一次循环达到题目所需要的要求。
	//先定义具体的指针。sum的值实际上就是数组从begin开始加到end为止
	//current_len统计的是从begin到end之间的数据有多少个
	//result返回最后的最短窗口 初始最大的int数,方便操作
	int begin = 0, end = 0;
	int sum = 0;
	int current_len = 0, result = INT32_MAX;
	//采用end++的变化
	for ( end = 0; end < nums.size(); end++)
	{
   
   
		sum += nums[end];
		//这里if注意一下,我们先按照自己感性的想法来写
		if (sum >= target)
		{
   
   
			//当中间的数字大了,我们实际上就可以统计窗口的大小,并且将begin进一位,然后再次统计了。
			current_len = end - begin + 1;
			//选择小的窗口大小
			result = result < current_len ? result : current_len;
			//准备下一次的循环,我们发现现在的数据已经大于target了,所以我们需要减掉begin的值,将左指针向右移动,然后后续右指针再进行移动窗口,得到最后的值
			sum -= nums[begin
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值