作者:小迅
链接:https://leetcode.cn/problems/shortest-subarray-with-sum-at-least-k/solutions/1925398/qian-zhui-he-dan-diao-shuang-duan-dui-li-zswz/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
题目![](https://img-blog.csdnimg.cn/377e5d7aa79a4f159d37d65445594f71.png)
示例![](https://img-blog.csdnimg.cn/4fa2a4970a9645c790826225bc809fd4.png)
思路
给你一个整数数组 nums 和一个整数 k ,找出 nums 中和至少为 k 的 最短非空子数组 ,并返回该子数组的长度。如果不存在这样的 子数组 ,返回 -1 。
nums 中和至少为 k 的 最短非空子数组 -> 可以转化为 前缀和
对于一个数组 nums[]={1,2,3,4,-1} 来说前缀和 sum[] = {0,1,3,6,10,9} 那么对于子数组[0,2)来说 和 就为 sum[2] - sum[0] = 3;我们一般取左闭右开所以长度为 2;
暴力法
我们知道了前缀和,可以使用双指针将所有前缀和两两组合,求前缀和差就为 子数组 的和,然后判断寻找符合条件的最小子数组,并保存即可。但是时间复杂度为 O(n^2)会超时得优化
单调 双端 队列优化
这里存在两个问题解决这两个问题,这问题也就不是什么问题了
- 为什么使用单调?
以下说的元素都是前缀和中的元素
情况一
对于任意一个前缀和元素,我们的目的是为了求子数组的和 并 判断是否并目标值大。所以当枚举到当前元素(前缀和数组中)时,我们可以就需要判断当前元素能否与前面的元素组成子数组并满足题目要求,那我们最简单的办法就是从头开始与子比较,当第一个元素与自身可以构成满足题目的子数组时,保存当前子数组长度,然后比较第二个,直到不能与自身构成满足题目的子数组为止,然后进行枚举下一个元素重复之前的步骤,可以发现这个就是暴力解法,但是对于下一个元素来说他还必要从头开始判断嘛??其实是不需要的因为下一个元素判断成功了也只是在上一个元素的基础上长度 + 1了,而且我们需要的是最小长度。所有当第一个元素匹配成功之后其实就可以将第一个元素弹出了,第二个元素也是一样。
情况二
如果当前元素比上一个元素小,那么当前前缀和 差 为 当前元素 - 上一个元素,肯定为一个 负数,如果这个负数也满足要求的话,那么对于任意一个正数也肯定满足要求,当后续出现任意一个比当前元素大的元素都可以满足题目要求,所以上一个元素也是没用的元素弹出,这里需要好好想一想
注意前缀和不是一个元素的子数组,是0-n个元素的子数组的集合
- 为什么使用双端队列?
因为需要对对头和队尾进行存在,所有需要用双端队列
代码
#define MIN(a, b) ((a) < (b) ? (a) : (b))
int shortestSubarray(int* nums, int numsSize, int k){
long sum[numsSize+1];
sum[0] = 0;
for(int i = 0; i < numsSize; i++)//记录前缀和
{
sum[i+1] = sum[i] + nums[i];
}
int queue[numsSize+1];
int left = 0;
int right = 0;
int len = INT_MAX;//初始化变量
for(int i = 0; i <= numsSize; i++)//枚举每一个前缀和元素
{
long cnt = sum[i];
while(left < right && cnt - sum[queue[left]] >= k)//情况一
{
len = MIN(len, i-queue[left]);
left++;
}
while(left < right && cnt <= sum[queue[right-1]])//情况二
{
right--;
}
queue[right++] = i;//保存当前前缀和下标
}
return len == INT_MAX ? -1 : len;
//官方的其实相当于把所有操作封装为了函数而已
}
作者:小迅
链接:https://leetcode.cn/problems/shortest-subarray-with-sum-at-least-k/solutions/1925398/qian-zhui-he-dan-diao-shuang-duan-dui-li-zswz/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。