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;
}
};