[LeetCode]Minimum Size Subarray Sum

题目:Minimum Size Subarray Sum

给定一个整数数组,和一个值,找到一个连续子序列,使其和>=给定的值,且该子序列长度最短。

思路1:

时间复杂度O(n),空间复杂度O(1)的解法。

使用首尾两个指针,求出两个指针之间的数据之和;当和大于等于s时,比较序列长度和最小值,然后,增加头指针;当和小于s时,然后,增加尾指针;

注意:

1.数组整个之和小于s的情况;

2.循环退出条件是tail != nums.cend();它不能保证最后的子序列是最短状态。

int LeetCode::minSubArrayLen(int s, vector<int>& nums){
    if (!nums.size())return 0;
    auto head = nums.cbegin(), tail = nums.cbegin() + 1;
    int min = nums.size(), cur = nums.at(0);//s可能是负值
    while (tail != nums.cend()){
        if (cur < s){//尾指针增加
            cur += *tail;
            ++tail;
        }
        else{
            if (min > tail - head)min = tail - head;//判断是否当前子序列更短
            cur -= *head;//头指针增加
            ++head;
        }
    }
    if (min == nums.size() && cur < s)return 0;//数组的总和仍小于s
    while (s <= cur){//最后子序列可能不是最优状态{7[2, 3, 1, 2, 4, 3]}
        cur -= *head;
        ++head;
    }
    if (min > tail - head + 1)min = tail - head + 1;
    return min;
}

思路2:

时间复杂度O(nlogn),空间复杂度O(1)的解法。

滑动窗口的思想来解此问题。固定一个大小的窗口,从头到尾滑动该窗口,判断窗口里的元素之和>=s,返回true。

然后,通过折半的方式锁定窗口的大小,找到最小的窗口大小使得窗口中的元素之和>=s。

int LeetCode::minSubArrayLen2(int s, vector<int>& nums){
    int i = 1, j = nums.size(), min = 0;
    while (i <= j) {
        int mid = (i + j) / 2;//窗口大小
        if (windowExist(mid, nums, s)) {
            j = mid - 1;//存在,则收缩窗口
            min = mid;
        }
        else i = mid + 1;//折半的方式锁定窗口大小
    }
    return min;
}

bool LeetCode::windowExist(int size, vector<int>& nums, int s) {
    int sum = 0;
    for (int i = 0; i < nums.size(); i++) {
        if (i >= size) sum -= nums[i - size];//开始滑动
        sum += nums[i];
        if (sum >= s) return true;
    }
    return false;
}

思路3:

时间复杂度O(nlogn),空间复杂度O(n)的解法。

这个思路需要辅助数组,用来保存给定的数组的前i项的数据之和。

循环所有元素,并且通过折半的方法找到当前位置开始到最近结束位置,使得该区间的元素之和>=s。

int LeetCode::minSubArrayLen3(int s, vector<int>& nums){
    int sum = 0, ret = nums.size() + 1;//ret记录最小值

    vector<int>& sums(nums);//前i项的和
    for (int i = 0; i < nums.size(); i++)
        sums[i] = nums[i] + (i == 0 ? 0 : sums[i - 1]);

    for (int i = 0; i < nums.size(); i++) {
        //找到以当前位置开始和大于s的最近结束位置
        int j = i, k = sums.size() - 1, offset = i == 0 ? 0 : sums[i - 1];//j从当前起始位置i开始,k作为尾指针前后往前,offset记录前一个的位置的和
        while (j <= k) {//折半的方式找到最近结束位置
            int m = (j + k) / 2;
            int sum = sums[m] - offset;
            if (sum >= s) k = m - 1;
            else j = m + 1;
        }
        if (j == nums.size()) break;
        ret = min(j - i + 1, ret);//更新最小值
    }

    return ret == nums.size() + 1 ? 0 : ret;
}
原文地址:https://www.cnblogs.com/yeqluofwupheng/p/6792536.html