刷题日记和大于等于target的最短子数组
Posted 傅耳耳
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了刷题日记和大于等于target的最短子数组相关的知识,希望对你有一定的参考价值。
和大于等于 target
的最短子数组
给定一个含有 n 个正整数的数组和一个正整数 target 。
找出该数组中满足其和 ≥ target
的长度最小的 连续子数组[nums_l, nums_l+1, ..., nums_r-1, nums_r]
,并返回其长度。如果不存在符合条件的子数组,返回 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 <= target <= 109
1 <= nums.length <= 105
1 <= nums[i] <= 105
思路一:前缀和 + 二分查找
-
预求出前缀和
sum[]
-
枚举每个位置
i
-
对每个位置
i
找位置bound
,使得i <= bound
&&nums[i...bound]
之和>= target
(即满足 sum[bound] - sum[i - 1]
的最小的 bound
值)
【合理性】
- 本题中均为正数,前缀和数组
sum[]
递增,保证了二分的正确性
代码:
class Solution
public:
int minSubArrayLen(int target, vector<int>& nums)
int len = nums.size();
// 预计算前缀和
int sum[len+1];
for(int i = 0;i < len;i ++ )
sum[i+1] = sum[i] + nums[i];
// 对每个位置i 进行二分查找 找右边界
int res = len + 1;
for(int i = 1;i <= len;i ++ )
int l = i, r = len;
while(l < r)
int mid = l + r >> 1;
if(sum[mid] - sum[i-1] < target) l = mid + 1;
else r = mid;
if(l <= len && sum[l] - sum[i-1] >= target) res = min(res, l-i+1);
if(res == len + 1) return 0;
return res;
;
时间复杂度:O(nlogn)
思路二:滑动窗口
连续子数组 ===> 滑动窗口确定左右边界 left、right
- 每轮迭代,窗口右边界扩大一步,
sum += nums[right]
增大sum
- 当
sum >= target
, 更新数组长度,且左边界不断增大left++
, 减小sum
,直到sum < target
, 过程中更新数组长度 - 重复上述步骤,直到窗口到达数组末尾
注:缩小窗口时的条件为 sum >= target
,因此left
不会跨越 right
,即 left <= right
成立,不需要另做判断
代码:
class Solution
public:
int minSubArrayLen(int target, vector<int>& nums)
int len = nums.size();
int left = 0, right = 0;
int sum = 0, res = len + 1;
while(right < len)
sum += nums[right];
// 缩小窗口
while(sum >= target)
res = min(res, right - left + 1);
sum -= nums[left];
left ++ ;
// 扩大窗口
right ++ ;
return res == len+1 ? 0 : res;
;
时间复杂度:O(n)
以上是关于刷题日记和大于等于target的最短子数组的主要内容,如果未能解决你的问题,请参考以下文章
leetcode剑指 Offer II 008. 和大于等于 target 的最短子数组
leetcode剑指 Offer II 008. 和大于等于 target 的最短子数组