题目描述:
输入一个整形数组,数组里有正数也有负数。
数组中连续的一个或多个整数组成一个子数组,每个子数组都有一个和。
求所有子数组的和的最大值。要求时间复杂度为O(n)。
例如输入的数组为1, -2, 3, 10, -4, 7, 2, -5,和最大的子数组为3, 10, -4, 7, 2,
因此输出为该子数组的和18。
算法一:O(n)
#include <iostream>
int maxSum(int* a, int n)
{
int sum=0;
int b=0;
for(int i=0; i<n; i++)
{
if(b<0)
b=a[i];
else
b+=a[i];
if(sum<b)
sum=b;
}
return sum;
}
int main()
{
int a[10]={1, -2, 3, 10, -4, 7, 2, -5};
cout<<maxSum(a,8)<<endl;
return 0;
}
/*-------------------------------------
解释下:
例如输入的数组为1, -2, 3, 10, -4, 7, 2, -5,
那么最大的子数组为3, 10, -4, 7, 2,
因此输出为该子数组的和18。
所有的东西都在以下俩行,
即:
b : 0 1 -1 3 13 9 16 18 13
sum: 0 1 1 3 13 13 16 18 18
其实算法很简单,当前面的几个数,加起来后,b<0后,
把b重新赋值,置为下一个元素,b=a[i]。
当b>sum,则更新sum=b;
若b<sum,则sum保持原值,不更新。
而sum一直都保存着最大的那个和值。
返回sum。
算法二:O(n*logn)
分治法:
情况1:这个满足最大和的子数组全部在本数组的左半部或者右半部。例如:左半部A[i]……A[n/2 - 1]或者右半部A[n/2]……A[j]。这种情况下可以直接使用递归调用。
情况2:满足最大和的子数组跨过了本数组的中间点。例如:A[i]……A[n/2-1] A[n/2]……A[j]连续。则这种情况下只要在左半部寻找以A[n/2-1]结尾,在右半部寻找以A[n/2]开头的两个满足最大和的连续数组,并求和即可。由于这个已知起点,只需要一个游标即可,所以复杂度是2*O(n/2)=O(n)。
综合以上两种情况,满足分治算法递归式:T(n)=2T(n/2)+O(n)=O(n*logn)
static int MaxSubSum(const int A[],int Left,int Right)
{
int MaxLeftSum,MaxRightSum; //左、右部分最大连续子序列值。对应情况1
int MaxLeftBorderSum,MaxRightBorderSum; //从中间分别到左右两侧的最大连续子序列值,对应情况2
int LeftBorderSum,RightBorderSum;
int Center,i;
if(Left == Right)Base Case
if(A[Left]>0)
return A[Left];
else
return 0;
Center=(Left+Right)/2;
MaxLeftSum=MaxSubSum(A,Left,Center);
MaxRightSum=MaxSubSum(A,Center+1,Right);
MaxLeftBorderSum=0;
LeftBorderSum=0;
for(i=Center;i>=Left;i--)
{ //求左边部分最大和
LeftBorderSum+=A[i];
if(LeftBorderSum>MaxLeftBorderSum)
MaxLeftBorderSum=LeftBorderSum;
}
MaxRightBorderSum=0;
RightBorderSum=0;
for(i=Center+1;i<=Right;i++)
{ //求右边部分最大和
RightBorderSum+=A[i];
if(RightBorderSum>MaxRightBorderSum)
MaxRightBorderSum=RightBorderSum;
}
//比较三种情况下最大值
int max1=MaxLeftSum>MaxRightSum?MaxLeftSum:MaxRightSum;
int max2=MaxLeftBorderSum+MaxRightBorderSum;
return max1>max2?max1:max2;
}
算法三:动态规划dp
取sum[i] 为前i个元素中,包含第i个元素且和最大的连续子数组,result 为已找到的子数组中和最大的。
对第i+1个元素有两种选择:1)放入前面找到的子数组;2)做为新子数组的第一个元素。所以有:
sum[i+1] = max( a[i+1], sum[i] + a[i+1] )
result = max( result, sum[i] )
#include <stdio.h>
int maxSum(int *arr, int length)
{
int sum = 0, result = 0;
for (int i = 0; i < length; i++)
{
sum = sum + arr[i] > arr[i] ? sum + arr[i] : arr[i];
result = sum > result ? sum : result;
}
return result;
}
int main()
{
int a[]={1, -2, 3, 10, -4, 7, 2, -5};
//int a[]={-1,-2,-3,-4}; //测试全是负数的用例
printf("%d\n", maxSum(a, 8));
return 0;
}
Reference:
1)http://blog.csdn.net/v_JULY_v/article/details/6444021
2)数据结构与算法分析c实现(p17 - p21 )