Leetcode-53: Maximum Subarray

53. Maximum Subarray

Medium

Given an integer array nums, find the 

subarray

 with the largest sum, and return its sum.

Example 1:

Input: nums = [-2,1,-3,4,-1,2,1,-5,4]
Output: 6
Explanation: The subarray [4,-1,2,1] has the largest sum 6.

Example 2:

Input: nums = [1]
Output: 1
Explanation: The subarray [1] has the largest sum 1.

Example 3:

Input: nums = [5,4,-1,7,8]
Output: 23
Explanation: The subarray [5,4,-1,7,8] has the largest sum 23.

Constraints:

  • 1 <= nums.length <= 105
  • -104 <= nums[i] <= 104

Follow up: If you have figured out the O(n) solution, try coding another solution using the divide and conquer approach, which is more subtle.

此题是经典中的经典题。

我的解法是设一个maxV和rangeSum。rangeSum是从某位置到当前位置的区间和(i..j)。max_v是迄今为止最大的rangeSum。

注意:

1) 当当前元素nums[i]>0时,
如果rangeSum已经<0,那么重起炉灶,rangeSum从nums[i]开始算(rangeSum=nums[i]);

否则rangeSum+=nums[i]。正上加正。

2) 当当前元素nums[i]<0时,

如果rangeSum+nums[i]是否>=0,如果是,那当前元素(虽然<0)还是要考虑进去的。比如说4,-1,2,1,里面的-1也要考虑。
如果rangeSum+nums[i]<0,那么我们可以分两种情况分析:

2.1 rangeSum<nums[i]<0,那么我们应该另起炉灶,rangeSum=nums[i]。

2.2 nums[i]<0<rangeSum,或nums[i]<rangeSum<0,那么rangeSum+nums[i]肯定比rangeSum小,那么这个nums[i]便没有必要加到rangeSum里面去。rangeSum就另起炉灶算了,让rangeSum=num[i]。因为之前的rangeSum已经和maxV比较过了。这个rangeSum=nums[i]比以前的rangeSum还小,但是没关系,它不会影响maxV,并且当后来的nums[i]>0时,rangeSum会更新为nums[i] (note 1处); 当后来的nums[i]<=0时,rangeSum不会发生note 2处的情况(因为rangeSum<0),而是只会到note 3处被更新为新的nums[i]。总之,这种情况对maxV不会影响。

所以两种 情况可以合并为rangeSum=nums[i]。

#include <iostream>
#include <vector>

using namespace std;

int maxSubArray(vector<int>& nums) {

    int rangeSum = nums[0];  //the sum of ranges
    int maxV=nums[0];    //the maximum value of the maximum sum that ends in the current number

    for (int i=1; i<nums.size(); i++) {
        if (nums[i]>0) {
            if (rangeSum<0) rangeSum=nums[i];    //note 1
            else rangeSum+=nums[i];
        }
        else if (nums[i]<=0) {
            if (rangeSum+nums[i]>=0) rangeSum+=nums[i];   //note 2
            else rangeSum=nums[i];    //note 3: no matter if rangeSum<nums[i] or not!
        }

        if (maxV<rangeSum) maxV=rangeSum;
    }

    return maxV;
}

int main()
{
    vector<int> nums={-2,1,-3,4,-1,2,1,-5,4};
    cout<<maxSubArray(nums)<<endl;

    vector<int> nums2={-1};
    cout<<maxSubArray(nums2)<<endl;

    return 0;
}

另一个帖子"求序列连续最大和问题"里面介绍了3种方法。第3种方法跟这个很像,但更简洁,我觉得更好。代码如下: 

int maxSubArray(vector<int>& nums) {

    int rangeSum=nums[0];  //the sum of ranges
    int maxV=nums[0];    //the maximum value of the maximum sum that ends in the current number

    for (int i=1; i<nums.size(); i++) {
        if (rangeSum<0) rangeSum=nums[i];
        else rangeSum+=nums[i];    //Note: no matter if rangeSum<nums[i] or not!
        if (maxV<rangeSum) maxV=rangeSum;
    }

    return maxV;
}

3刷

class Solution {
public:
    int maxSubArray(vector<int>& nums) {
        int n = nums.size();
        int maxSum = INT_MIN;
        
        int index = 0;
        int sum = 0;
        while(index < n) {
            sum += nums[index];
            if (sum > maxSum) {
                maxSum = sum;
            }
            if (sum <= 0) {
                sum = 0;
            } 
            index++;
        }
        return maxSum;
    }
};

代码同步在
GitHub - luqian2017/Algorithm: LintCode, LeetCode, Uva, etc

这种方法等价于Kadane 算法:

class Solution {
public:
    int maxSubArray(vector<int>& nums) {
        int n = nums.size();
        int sum = 0, res = INT_MIN;
        for (auto num : nums) {
            sum = max(sum + num, num);
            res = max(res, sum);
        }
        return res;
    }
};

解法4:
注意:虽然nums里面有负数,这题也可以用滑动窗口。时间复杂度也是O(n)。

为什么这个方法对?当sum>0时,我们会增加右窗口,直到sum<0,此时我们增加左窗口,直到sum>=0。以[-2,1,-3,4,-1,2,1,-5,4]为例,一开始sum=-2,right++,left++,sum=0,并且res已经记下sum值。然后sum=1,right++,left不动,res更新。然后sum=-2,right++,left++。然后left++。然后sum=4, right++,res更新。然后sum=3,right++,res更新。然后sum=5,right++,res更新。然后sum=6,right++,res更新。后面的情况差不多,就不写了。

class Solution {
public:
    int maxSubArray(vector<int>& nums) {
        int n = nums.size();
        int left = 0, right = 0;
        int sum = 0, res = INT_MIN;
        while (right < n) {
            sum += nums[right];
            res = max(res, sum);
            right++;
            while (sum < 0) {
                sum -= nums[left];
                left++;
            }
        }
        return res;
    }
};

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值