D3 - 209 长度最小子数组
209 长度最小子数组(力扣:https://leetcode.cn/problems/minimum-size-subarray-sum/
条件:在乱序数组内找到满足总和>=target且长度最小的子数组,return 子数组长度;
Tips:
-
思路在看到tips要用滑动窗口后就确定了,即左边界定、右边界扩到第一个可能的解,而后保证这个窗口长度移动算sum(左++右++),直到新sum大于旧sum,再看能不能缩小窗口(左加右定);
-
问题:
边界条件确定错,因为逻辑是当发现新sum大于旧sum时再缩小窗口直到sum刚好满足>=target,所以直接实现后面的条件就包括前面的了;
if+for和while的使用混淆,具体见注释,此代码只完成了样例,测试集内未通过,因为缺少了极限情况right触达边界时,这也是写太复杂容易遗漏判定条件:初版代码:
点击查看代码
class Solution {
public:
int minSubArrayLen(int target, vector<int>& nums) {
int L = 0, s = 0, e = 0, sum = 0;
while(e < nums.size() && sum < target){
sum += nums[e];
e++;
}
if(sum < target) return 0;
else{
L = e - s + 1;//记录第一个成功的长度,前提是L存在
//窗口保持长度不变,向右滑动直到当前值比sum大(意味着可以缩小窗口了)
while( e < nums.size()){
//这里不要用if-else,因为缩窗口要直到缩最小,所以缩要是while,而移动的时候是只要不是while缩就一直动,所以放到第一层while这里执行就可以了
sum = sum - nums[s] + nums[e];
s++;
e++;
while( sum >= target){ //如果新窗口大则从窗口头开始缩小
sum -= nums[s];
s++;
}
L = e - s + 1;
}
}
return L;
}
};
Deepseek补充完边界条件后可以跑通,但是实际更换场景后很容易遗漏一些边界情况,比如最后right到边界的情况:
点击查看代码
class Solution {
public:
int minSubArrayLen(int target, vector<int>& nums) {
int n = nums.size();
int left = 0, right = 0;
int sum = 0;
int minLen = INT_MAX;
// 步骤1:找到第一个解
while (right < n && sum < target) {
sum += nums[right];
right++;
}
// 如果根本没找到解
if (sum < target) return 0;
// 第一个窗口的长度
minLen = right - left;
// 步骤2:固定长度滑动并尝试优化
while (right < n) {
// 窗口整体右移:去左加右
sum = sum - nums[left] + nums[right];
left++;
right++;
// 步骤3:尝试缩小窗口(从左边缩小)
while (sum - nums[left] >= target) {
sum -= nums[left];
left++;
minLen = min(minLen, right - left);
}
}
// 最后再尝试缩小一次(处理right到边界的情况)
while (sum >= target) {
minLen = min(minLen, right - left);
sum -= nums[left];
left++;
}
return minLen;
}
};
更新后的动态窗口,难点是提炼变动窗口的条件和边界(如只要是没有达到数组末尾窗口就可以一直扩大,只有出现满足条件的子数组才更新len),以及该使用什么样的语句(如while在这里等效于if+for但是更简洁和保证多次进入循环),个人比较容易遗忘的两点是数组长度为right - left + 1以及缩小窗口前先更新len,这样缩完如果不再满足target也不会污染之前存储的len:
点击查看代码
class Solution {
public:
int minSubArrayLen(int target, vector<int>& nums) {
int n = nums.size();
int left = 0; // 窗口左边界
int sum = 0; // 当前窗口的和
int minLen = INT_MAX; // 最小长度,初始设为最大值
for (int right = 0; right < n; right++) {
// 扩展窗口:加入右边界元素
sum += nums[right];
// 当窗口满足条件时,尝试缩小窗口
while (sum >= target) {
// 更新最小长度
minLen = min(minLen, right - left + 1);
// 缩小窗口:移除左边界元素
sum -= nums[left];
left++;
}
}
// 如果找到了符合条件的子数组,返回最小长度;否则返回0
return minLen == INT_MAX ? 0 : minLen;
}
};
浙公网安备 33010602011771号