Find the contiguous subarray within an array (containing at least one number) which has the largest sum.
For example, given the array [-2,1,-3,4,-1,2,1,-5,4],
the contiguous subarray [4,-1,2,1] has the largest sum = 6.
解法一
枚举法,找到每个元素开始的前缀和,逐个比较找出最大的即为前缀和,结果超时,O(n^2)
int maxSubArray(vector<int>& nums) {
int max_sum = nums[0];
for (int i = 0; i < nums.size(); i++) {
int tmp=0;
for(int j=i;j<nums.size();j++){
tmp=tmp+nums[j];
max_sum=max(max_sum,tmp);
}
}
return max_sum;
}
解法二
分治法,最大连续子序列可能存在的三个区间:
1. 完全在nums[left, mid-1]
2. 完全在nums[mid+1, right]
3. 包含 nums[mid],然后向左右扩展
最后比较左子序列、右子序列和中间序列,得到最大和。时间复杂度O(nlogn),通过
int maxSubArray(vector<int>& nums) {
return divide(nums, 0, nums.size()-1);
}
int divide(vector<int>& nums, int left, int right) {
if (left == right)
return nums[left];
if (left+1 == right)
return max(nums[left]+nums[right], max(nums[left], nums[right]));
int mid = (left+right)/2;
int left_max = divide(nums, left, mid-1);
int right_max = divide(nums, mid+1, right);
// 求中间连续子序列的最大和
int mid_max = nums[mid];
// 向左扩展
int temp = mid_max;
for (int i = mid-1; i >= left; i--) {
temp += nums[i];
mid_max = max(temp, mid_max);
}
// 向右扩展
temp = mid_max;
for (int j = mid+1; j <= right; j++) {
temp += nums[j];
mid_max = max(temp, mid_max);
}
return max(mid_max, max(left_max, right_max));
}
解法三
动态规划
当从头遍历数组元素时,对于数组中的一个整数有以下两种情况,加入之前的subArray,或者自己另起一个新的subArray,对应情况如下:
当之前subArray 的总和大于 0 时,我们认为 其对后续结果是有贡献的,这种情况下,我们选择加入之前的subArray
当之前subArray 的总和小于等于0时,我们认为其对后续结果是没有贡献的,这种情况下,我们选择以当前数字开始,另起一个subArray
设状态f(j) 表示 以 nums[j] 为结尾的最大连续子序列的和,则状态转移方程如下:
f(j) = max{f(j)+nums[j], nums[j]},其中1<=j<=n
target = max{f(j)}, 其中1<=j<=n
(此处状态转移方程不是很明白,作为转载暂时放上来)
int maxSubArray(vector<int>& nums) {
int result = nums[0];
int sum = 0;
for (int i = 0; i < nums.size(); i++) {
sum = max(sum+nums[i], nums[i]);
result = max(result, sum);
}
return result;
}
某些错解
其一
连续子序列的和等于两个前缀和之差。空间复杂度O(n),时间复杂度O(n),这种方式只对于正整数序列有效,当出现负数时结果就会有误,例如:
input: | Output: | Expected: |
---|---|---|
[-2,-1] | 1 | -1 |
代码如下
int maxSubArray(vector<int>& nums) {
if(nums.size()==1) return nums[0];
else{
int s[nums.size()];
s[0]=nums[0];
int maxs=s[0],mins=s[0];
for (int i = 1; i < nums.size(); i++) {
s[i]=s[i-1]+nums[i];
maxs=max(maxs,s[i]);
mins=min(mins,s[i]);
}
return maxs-mins;
}
}
其二
int maxSubArray(vector<int>& nums) {
int max_sum = nums[0];
int tmp=max_sum;
for (int i = 0; i < nums.size(); i++) {
tmp=nums[i];
for(int j=i+1;j<nums.size();j++){
tmp=tmp+nums[j];
max_sum=max(max_sum,tmp);
}
max_sum=max(max_sum,tmp);
}
return max_sum;
}
枚举法时的细节考虑出错,内层循环应该从i开始,此处错解当做从i+1开始,导致出现边界元素或者单独一个元素作为最大子串和的时候结果出错。
如
input: | Output: | Expected: |
---|---|---|
[-1,0,-2] | -1 | 0 |
以及
input: | Output: | Expected: |
---|---|---|
[-2,1] | -1 | 1 |
参考:
https://github.com/soulmachine/leetcode
http://blog.csdn.net/quzhongxin/article/details/46603957
http://www.cnblogs.com/gj-Acit/archive/2013/02/12/2910332.html