D3 - 209 长度最小子数组

209 长度最小子数组(力扣:https://leetcode.cn/problems/minimum-size-subarray-sum/

条件:在乱序数组内找到满足总和>=target且长度最小的子数组,return 子数组长度;
Tips:

  1. 思路在看到tips要用滑动窗口后就确定了,即左边界定、右边界扩到第一个可能的解,而后保证这个窗口长度移动算sum(左++右++),直到新sum大于旧sum,再看能不能缩小窗口(左加右定);

  2. 问题:
    边界条件确定错,因为逻辑是当发现新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;
    }
};
posted @ 2026-01-06 21:36  SCONLY  阅读(9)  评论(0)    收藏  举报