双索引技术
滑动窗口:两个索引表示的是一个窗口,我们让这个窗口不停的滑动,在数组中找到我们希望求的解。
leetcode 209
给定一个含有 n 个正整数的数组和一个正整数 s ,找出该数组中满足其和 ≥ s 的长度最小的 连续 子数组,并返回其长度。如果不存在符合条件的子数组,返回 0。
示例:
输入:s = 7, nums = [2,3,1,2,4,3]
输出:2
解释:子数组 [4,3] 是该条件下的长度最小的子数组。
进阶:
如果你已经完成了 O(n) 时间复杂度的解法, 请尝试 O(n log n) 时间复杂度的解法。
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/minimum-size-subarray-sum
题目示例分析:在数组中4,3满足sum>=s的条件而且是最短的,比如说1,2,4也满足条件但是是3个元素了,2,3,1,2这个连续子数组也满足条件 但是长度为4,在整个数组中4,3是最小的返回2.
思路:首先要先明白子数组的概念 子数组是数组中的部分元素可以是连续的也可以不连续本题中是连续子数组。还要考虑没有解怎么办,本题中返回0.。可以先考虑暴力求解遍历所有连续子数组[i,j],每一个起始的连续子数组都是被起始索引i和终止索引j决定的,我们需要使用双层循环一旦我们得到了一个连续子数组从i到j这个范围,我们还需要把范围内所有numbers相加起来计算和sum,验证sum>=s,如果验证成功则是满足条件的子数组,再看长度是不是比当亲、按找到的解小,时间复杂度O(n^3)。
优化:暴力解中存在大量重复操作 比如说遍历到了nums[i...j],我们可以很快的知道nums[i...j-1]的和只需要减去j上的元素即可。基于以上想法,可以设计出nums[i...j]的一个连续子数组。如果子数组和小于s把下一个元素拉进来 j++看nums[i...j+1]的和是否大于等于s,还不满足,继续向后看,以此类推。直到sum>s就找到了满足题意的连续子数组,把长度记录下来。从i这端开始缩小连续子数组i--,这样连续子数组的和就会小一些,到某一时刻sum<s,此时j索引继续向前行进找到一个连续子数组的和大于s,依次类推,如图这个窗口不是固定的是由i和j控制的,窗口不停的向前滑动来找到满足题意的连续子数组。这就是滑动窗口的意思。
#define min(a,b) a<b?a:b
int minSubArrayLen(int s,int *nums,int numsSize)
{
//时间复杂度O(n)索引l r 不断地向前推进遍历了一遍数组 空间复杂度O(1) 没有开辟新的数组
int l = 0,r = -1;//nums[l...r]为我们的滑动窗口,l=0 r=-1 初始为空
int sum = 0;//窗口中元素的和
int res = numsSize+1;//来记录当前找到最小连续子数组的长度 初始化为整个数组
//长度+1 因为之后要逐渐求最小值,所以初始化设置为最大的 ,这个值是不可能取到的
//一旦找到了一个满足题意的连续子数组只需要求 res和当前的长度求最小值就行了
while(l<numsSize)//左边界能取值右边界就能取值 还存在可能有的滑动窗口供我们选择
{
if(r+1<numsSize&&sum<s)//r+1<numsSize 如果r已经到了最右侧就不能向前扩展了
{
r++;
sum+=nums[r];
// 也可以写成 sum+=nums[++r];
}
else
{
// 也可以写成 sum+=nums[l++];
sum-=nums[l];
l++;
}
if(sum>=s)//找到了一个连续子数组 nums[l...r]
{// 把res和当前连续子数组长度作比较求最小值
res = min(res,r-l+1);//不断获得连续子数组的最小值
}
}
if(res == numsSize+1)//遍历一边数组没有解的情况 res没有更新也就是没有解
{
return 0;
}
return res;
}