【LeetCode】209.Minimum Size Subarray Sum(长度最小的子数组)-C++实现

本题是facebook 的面试题。

问题描述:

一、第一种方法:暴力解法

该方法在 Leetcode 中会超时!

// 时间复杂度: O(n^3)
// 空间复杂度: O(1)
class Solution {
public:
    int minSubArrayLen(int s, vector<int>& nums) {

        assert(s > 0);

        int res = nums.size() + 1;
        for(int l = 0 ; l < nums.size() ; l ++)
            for(int r = l ; r < nums.size() ; r ++){
                int sum = 0;
                for(int i = l ; i <= r ; i ++)
                    sum += nums[i];
                if(sum >= s)
                    res = min(res, r - l + 1);
            }

        if(res == nums.size() + 1)
            return 0;

        return res;
    }
};

分析:

使用两个指针 l 和 r 双重遍历数组,

1)当 l = 0 时,使用 r 遍历数组:

2)当 l = 1  时,使用 r 遍历数组:

3)一次类推,知道指针 l 遍历完成:

由于每移动一次指针 r 都需要 for 遍历数组求和sum, 所以时间复杂度为 O(n^3)

 

第二种方法:活动窗口法

活动窗口法分析:

①设定两个指针 i 和 j , 将数组区间nums[ i ... j] 看成窗口,

 ②if(j  + 1 < nums.size() && sum < s) 

   则 j ++ ;

这时 sum += nums[ j ];

③ 当 sum > s 

  则 sum -= nums[ i ];

 

 

④当 sum < s,则移动指针 j

 

实现代码:

// 时间复杂度: O(n)
// 空间复杂度: O(1)
class Solution {
public:
    int minSubArrayLen(int s, vector<int>& nums) {

        assert(s > 0);

        int l = 0 , r = -1; // nums[l...r]为我们的滑动窗口
        int sum = 0;
        int res = nums.size() + 1;

        while(l < nums.size()){   // 窗口的左边界在数组范围内,则循环继续

            if(r + 1 < nums.size() && sum < s){
				++r;
                sum += nums[r];
			}
            else {// r已经到头 或者 sum >= s
                sum -= nums[l];
				l++;
			}

            if(sum >= s)
                res = min(res, r - l + 1);
        }

        if(res == nums.size() + 1)
            return 0;
        return res;
    }
};


int main() {

    int nums[] = {2, 3, 1, 2, 4, 3};
    vector<int> vec(nums, nums + sizeof(nums)/sizeof(int));
    int s = 7;

    cout << Solution().minSubArrayLen(s, vec) << endl;

    return 0;
}

第三种方法:活动窗口法的另一种实现方法

实现代码:

//时间复杂度:O(n) 
class Solution {
public:
    int minSubArrayLen(int s, vector<int>& nums) {
        int n = nums.size(), start = 0, sum = 0, minlen = INT_MAX;
        for (int i = 0; i < n; i++) { 
            sum += nums[i]; 
            while (sum >= s) {
                minlen = min(minlen, i - start + 1);
                sum -= nums[start++];
            }
        }
        return minlen == INT_MAX ? 0 : minlen;
    }
};

  你可能会感到奇怪,有一个for循环和一个while 循环,它怎么可能是O(n)。关键是,while循环在每个启动位置开始时最多执行一次。然后开始增加1,while循环移动到下一个元素。因此,在整个for循环从0到n - 1的过程中,循环在大多数O(n)次中运行。因此,for循环和while循环都有O(n)时间复杂度,总体运行时间为O(n)。

你可以看一下整个遍历过程:

1)当 i = 0 时,sum = 2;

 

 2)当 i = 1 时,sum = 5;

  3)当 i = 2 时,sum = 6;

 4)当 i = 3 时,sum = 8; 记录此时的 sum > 7 时的数组个数:minlen = 4;

  此时,sum > s=7; 将start右移一位:

 4)此时,sum = 6;将 i 右移一位:

此时,sum = 10 > s;   记录此时的 sum > 7 时的最小数组个数:minlen = 4;

将start右移一位:

此时,sum = 7,仍然大于等于 s = 7;记录此时的 sum > 7 时的最小数组个数:minlen = 3;

 继续将start右移一位:

5)此时,sum = 6,将指针 i 向右移一位:

 

此时,sum = 9 ,大于s = 7; 记录此时的 sum > 7 时的最小数组个数:minlen = 3;

然后将start右移一位:

此时,sum = 7 ,大于等于s = 7; 记录此时的 sum > 7 时的最小数组个数:minlen = 2; 

然后将指针start右移一位:

此时,sum = 5,然后i ++.

整个数组遍历完成! 

<本文完>

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值