209. 长度最小的子数组(C++)
题目
给定一个含有 n 个正整数的数组和一个正整数 s ,找出该数组中满足其和 ≥ s 的长度最小的 连续 子数组,并返回其长度。如果不存在符合条件的子数组,返回 0。
示例:
输入:s = 7, nums = [2,3,1,2,4,3]
输出:2
解释:子数组 [4,3] 是该条件下的长度最小的子数组。
进阶:
- 如果你已经完成了 O(n) 时间复杂度的解法, 请尝试 O(n log n) 时间复杂度的解法。
分析与题解
暴力解法
循环嵌套,以数组的每个元素为起点,不断累加直至元素和sum >= s
,这是我们再记录子序列元素个数的长度。每次内层循环后都需要跟当前最小子序列长度进行比较。
完整代码如下:
class Solution {
public:
int minSubArrayLen(int s, vector<int>& nums) {
int n = nums.size();
int minLen = INT_MAX;
int subLen = 0;
for (int i = 0; i < n; i++) {
int sum = 0;
for (int j = i; j < n; j++) {
// 设置中止条件
sum += nums[j];
if (sum >= s) {
subLen = j - i + 1;
minLen = min(minLen, subLen);
break;
}
}
}
// 最后筛选没有可行长度的极端情况
return minLen == INT_MAX ? 0 : minLen;
}
};
滑动窗口
类似双指针法的变种,所谓滑动窗口,「就是不断的调节子序列的起始位置和终止位置,从而得出我们要想的结果」。
滑动窗格内包含的就是满足sum >= s
的子序列,核心代码如下:
while (sum >= s) {
subLen = j - i + 1;
minLen = min(minLen, subLen);
// 满足条件后移动滑动窗口的左边界
sum -= nums[i];
i++;
}
初始找到一个符合条件的滑动窗格后,考虑依次删除窗格左侧的元素并使用while
循环再次判断题目条件。当退出内层while循环后,再从数组的下一个起点开始进行讨论。对应于暴力解,可以理解为在外层循环中穿插的移动滑动窗口左侧边界,来减少算法复杂度。
完整代码如下:
class Solution {
public:
int minSubArrayLen(int s, vector<int>& nums) {
int n = nums.size();
// i表示滑动窗口的左边界下标
int i = 0;
int minLen = INT_MAX;
int subLen;
int sum = 0;
for (int j = 0; j < n; j++) {
sum += nums[j];
while (sum >= s) {
subLen = j - i + 1;
minLen = min(minLen, subLen);
// 满足条件后移动滑动窗口的左边界
sum -= nums[i];
i++;
}
}
// 如果minLen没有赋值,说明没有符合条件的子序列
return minLen == INT_MAX ? 0 : minLen;
}
};