Leetcode第862题:和至少为K的最短子数组(Shortest Subarray with sum at least k)

解题思路

前缀和

定义前缀和s[0]=0, s[i+1]=j=0inums[j]
例如 nums=[1,2,1,2],对应的前缀和数组为s=[0,1,3,2,4]

通过前缀和,可以将子数组和转换为两个前缀和的差,即
j=leftrightnums[j]=j=0rightnums[j]j=0left1nums[j]=s[right+1]s[left]

例如nums的子数组[2,1,2]的和就可以用s[4]s[1]=41=3计算出来.
此时就可以通过枚举所有i>js[i]s[j]k的子数组[j,i),取其中最小的ij作为答案.

单调队列

枚举所有前缀和子数组的时间复杂度是O(n2),使用某个数据结构维护遍历过的s[i],及时移除无用的s[i].

第一,当遍历到s[i]时,如果左边存在某个s[j],满足s[i]s[j]k,那么无论s[i]右边的数字大小,都不会把j当作子数组的左端点,然后得到一个比ij更短的子数组.因此可以将s[j]移除.

第二,如果s[i]s[j],假如后续有数字x能和s[j]组成满足要求的子数组,即xs[j]k,那么必然也有xs[i]k,由于从s[i]x的子数组更短,因此可以将s[j]移除.

做完这两个优化后,再把s[i]加到这个数据结构中.第二个优化保证了数据结构中的s[i]会形成一个递增的序列,因此第一个优化移除的是序列最左侧的若干元素,第二个优化移除的是最右侧的若干元素.因此该数据结构满足移除最左端元素和最右端元素,以及在最右端添加元素,故选用双端队列.元素保持单调递增的双端队列也成为单调队列.

核心代码如下:

int shortestSubarray(vector<int> &nums, int k) {
int n = nums.size(), ans = n + 1;
long s[n + 1];
s[0] = 0L;
for (int i = 0; i < n; ++i)
s[i + 1] = s[i] + nums[i]; // 计算前缀和
deque<int> q;
for (int i = 0; i <= n; ++i) {
long cur_s = s[i];
while (!q.empty() && cur_s - s[q.front()] >= k) {
ans = min(ans, i - q.front());
q.pop_front(); // 优化一
}
while (!q.empty() && s[q.back()] >= cur_s)
q.pop_back(); // 优化二
q.push_back(i);
}
return ans > n ? -1 : ans;
}
posted @   hql5  阅读(34)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· C#/.NET/.NET Core优秀项目和框架2025年2月简报
· Manus爆火,是硬核还是营销?
· 一文读懂知识蒸馏
· 终于写完轮子一部分:tcp代理 了,记录一下
点击右上角即可分享
微信分享提示