LeetCode209. 长度最小的子数组

2021-05-02 07:29

阅读:312

标签:nlogn   个数   int start   双指针   +=   辅助   for循环   复杂   暴力枚举   

技术图片

方法一

暴力枚举所有可能的子数组,也就是枚举子数组的所有开始下标和结束下标,计算子数组的和,如果子数组的和小于等于s,就更新最小长度。

class Solution {
public:
    int minSubArrayLen(int s, vector& nums) {
        if(nums.size() == 0) {                  //数组为空的情况需要特判
            return 0;
        }
        int res = INT_MAX;                      //最终的返回结果,初始化为一个较大的数
        for(int i = 0; i = s) {
                    res = min(res, j -i + 1);          //如果子数组的和小于等于s,就更新res
                    break;                      //不要忘了加 break !!!
                }
            }
        }
        return res == INT_MAX ? 0 : res;
    }
};

方法二

同样是枚举子数组的起始下标,我们考虑优化子数组结束下标的搜索空间,如果能用二分找到(满足子数组的和大于等于s)子数组结束下标的位置,相比于方法一就会更快,
二分查找要求数组是单调的,由于题目保证每一个元素都是正的,所以我们考虑用一个sums数组记录前缀和,也就是说,sums[i]表示nums[0] ~ nums[i - 1]的元素总和,
这样,问题就转化为对于每一个起始下标i,通过二分查找寻找一个大于等于i的最小下标bound,使得sums[bound] - sums[i - 1] >= s,sums[bound] - sums[i - 1]就是子数组的和。找到了bound之后,我们就更新子数组的最小长度(此时子数组的长度是bound - (i - 1))。

class Solution {
public:
    int minSubArrayLen(int s, vector& nums) {
        if(nums.size() == 0) {
            return 0;
        }
        int size = nums.size();
        vector sums(size + 1, 0);            //这里sums的大小为size + 1,是因为我们要让sums[0]为0,方便下一句的for循环对suns做初始化
        for(int i = 1; i = target,那么以i为起始下标,到bound的子数组的和就大于等于sum
            auto bound = lower_bound(sums.begin(), sums.end(), target);
            if(bound != sums.end()) {            //如果bound不越界,就更新子数组的最小长度
                res = min(res, static_cast((bound - sums.begin()) - (i - 1)));   
            }
        }
        return res == INT_MAX ? 0 : res;
    }
};

方法三

考虑用双指针,两个指针start和end分别表示子数组的开始和结束下标,变量sum表示以start开头,以end结尾的子数组的和。start, end, sum的初始值都为0.
先将nums[end]加入sum中,如果sum = s,此时不增加end,而是更新子数组的最小长度(此时长度为end - start + 1),然后不断增加start(并更新子数组的最小长度)直到sum

class Solution {
public:
    int minSubArrayLen(int s, vector& nums) {
        if(nums.size() == 0) {
            return 0;
        }
        int start = 0, end = 0, sum = 0, res = INT_MAX;
        while(end = s) {
                res = min(res, end - start + 1);
                sum -= nums[start];
                ++start;
            }
            ++end;
        }
        return res == INT_MAX ? 0 : res;
    }
};

复杂度分析

  1. 暴力方法中枚举起始下标需要O(n),内层循环枚举结束下标又需要O(n),因此时间复杂度是O(n);由于无需额外的辅助数组,因此空间复杂度是O(1);
  2. 前缀和 + 二分查找同样需要O(n)来枚举起始下标,但这里对于子数组的结束下标使用了二分查找进行优化,查找的复杂度是O(logn),因此总的时间复杂度是O(nlogn);由于需要一个额外数组sums存放前缀和,因此空间复杂度是O(n);
  3. 双指针方法中,左右指针start和end都只会向右移动,并且对于每个起始下标,无需从头开始枚举结束下标,因此时间复杂度是O(n);双指针方法也无需额外的辅助数组,空间复杂度为O(1).

LeetCode209. 长度最小的子数组

标签:nlogn   个数   int start   双指针   +=   辅助   for循环   复杂   暴力枚举   

原文地址:https://www.cnblogs.com/linrj/p/13204659.html


评论


亲,登录后才可以留言!