代码随想录day3-长度最小的子数组

1、LeetCode 209 长度最小的子数组

题目分析:
初看这个题目,可以使用暴力解法来解决,主要思路如下:
外层循环遍历数组的每个元素,内层循环从这个元素之后往后面遍历,并累计求和,一旦发现和sum大于target,此时就可以得到这个子数组的长度,不断更新。

这里的更新方法很巧妙:我们需要一个最小值,那么我们就设置一个result=很大,这个很大就是int数值类型的上线INT32_MAX。一旦我们得到的子数组的长度小于这个result,那么就不断更新即可。
我们这里也使用了三目运算符进行更新,因为三目运算符比if-else更快。

但是!!!,暴力算法在本题中超时了,因此要使用一些别的方法。
由于本质上还是要遍历数组所有的元素,我们还是使用双指针法来进行尝试,并且称这个双指针法为滑动窗口法

题目解答:
方法一:暴力法(超出时间限制)

class Solution {
public:
    int minSubArrayLen(int target, vector<int>& nums) {
        // 使用暴力解法
        int sum = 0;
        int subLen = 0;  // 子数组的长度
        int result = INT32_MAX;  // 最终的结果

        for (int i = 0; i < nums.size(); i++) {
            sum = 0;  // 每一个循环里面sum都要等于0
            for (int j = i; j < nums.size(); j++) {
                sum += nums[j];
                if (sum >= target) {
                    subLen = j - i + 1;
                    // 这里巧妙地使用了一个三目运算符,并且设置了int的上限
                    result = result < subLen ? result : subLen;
                    break;
                }
            }
        }
        return result == INT32_MAX ? 0 : result;  // 最后的返回也是使用三目运算符
    }
};

方法二:滑动窗口法(双指针法)
滑动窗口法的精华是动态更新窗口的界限以及窗口内部的和。

class Solution {
public:
    int minSubArrayLen(int target, vector<int>& nums) {
        int winLeft = 0;  // 窗口左端
        int winRight = 0;  // 窗口右端
        int result = INT32_MAX;  // 最终的结果
        int sum = 0;  // 窗口内元素的和
        int subLen = 0;

        while (winRight < nums.size()) {
            sum += nums[winRight];
            // 注意这里为什么是while而不是if,一定要想想清楚
            while (sum >= target) {
                subLen = winRight - winLeft + 1;  // 计算滑动窗的长度
                sum -= nums[winLeft++];  // 这里是滑动窗口的精华所在
                result = result < subLen ? result : subLen;  // 进行结果的更新
            }
            winRight++;  // 滑动窗口右边界更新
        }
        // 如果result没动
        return result == INT32_MAX ? 0 : result;
    }
};

这里的难点就在于滑动窗的更新那里,使用的while而不是if,下面举例说明:
如果数组为 1 1 1 1 100, target = 101, 那么当left在第一个1的时候就已经满足105 > 101了,此时得到的子数组长度是5,但是明显最小的字数粗长度是2,所以这种情况就错误了!
必须使用while而不是if。

即使使用while作为判断条件,算法的时间复杂度还是O(n),空间复杂度O(1)。
这个时间复杂度我之后再研究研究。

day3的内容23日还需要进行补充!
203.2.23日进行补充

2、LeetCode 209 长度最小的子数组

题目分析:
本题还是牵涉到遍历数组的问题,还是可以使用滑动窗口来解决。具体的思路在代码中进行体现。

class Solution {
public:
    int totalFruit(vector<int>& fruits) {
        set<int> classes;  // 存放水果的种类
        int fast = 0;  // 快指针,依次遍历数组中的所有元素,一旦出现第三个类别,回退到第二个
        int slow = 0;  // 慢指针,始终指向第一个类,方便fast回退
        int type = 0;  // 篮子中水果的种类
        int result = 0;  // 最终的结果
        int subLen = 0;  // 子数组的长度

        while (fast < fruits.size()) {
            classes.insert(fruits[fast]);
            type = classes.size();
            if (type <= 2) {
                subLen++;  // 子数组的长度增加
                if (type == 1) slow++;  // slow只会指向第一个类
                fast++;  // 继续往后遍历
                result = result > subLen ? result : subLen;
            }
            else {
                // 此时的slow由于刚刚经过了++,现在已经指向第二个类了
                subLen = 0;
                fast = slow - 1;  // 快指针回退到慢指针的位置,也就是第二个类了,但是待会儿还要加1,所以这里减1
                classes.erase(classes.begin(), classes.end());  // 清除类别列表
            }
        }
        return result == 0 ? 0 : result;
    }
};

3、LeetCode 76 最小覆盖字串

本题还需要思考,做出来了更新。

总结:今天涉及的题目解决的方法是滑动时间窗,本质上还是双指针的应用。
注意第一个题的时间窗的更新,用的是while作为判断条件,而不是if,想明白原因。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值