对于这个问题我们给出三种算法:
算法1:T(N) = O(N^2)
一种笨办法,两个for循环,起点从下标0开始,每遍历一次就加1再遍历,一直加到N,这样就得到了最后的结果,思路不难但是不算好。
代码如下:
int MaxSubSequenceSum(int A[],int N){
int ThisSum,MaxSum;
MaxSum = 0;
for(int i = 0; i < N; i++){
ThisSum = 0;
for(int j = i; j < N; j++){
ThisSum += A[j];
if(ThisSum > MaxSum)
MaxSum = ThisSum;
}
}
return MaxSum;
}
算法2:T(N) = O(N)
该算法的优点是它只对数据进行一次扫描,一旦完成对A[ i ]的读入和处理,就不再需要记忆它了,具有这种特性的算法叫做联机算法(on-line algorithm)
代码如下:
int submax2(int A[],int N){
int ThisSum,MaxSum;
ThisSum = MaxSum = 0;
for(int i = 0; i < N; i++){
ThisSum += A[i]; //向右累加
if(ThisSum > MaxSum)
MaxSum = ThisSum; //发现更大的则更新当前结果
else if(ThisSum < 0) //如果当前子列和为负,则不可能使后面的部分增大,抛弃掉
ThisSum = 0;
}
return MaxSum;
}
算法3:T(N) = O(N logN)
分治法:将问题分成两个大致相等的子问题,然后递归地对它们求解。
对于这个问题,最大子序列和可能在三处出现,将序列分成两半部分,则最大子序列可能全在左半部分,可能全在右半部分,又或者它横跨这两部分。有了这个思路,我们就可以用递归来求解,虽然相对复杂,但是体现出了递归的威力。
代码如下:
int Max3(int a,int b,int c){
return a>b?(a>c?a:c):(b>c?b:c); //返回三个整数中的最大值
}
int MaxSubSum(int A[],int Left,int Right){
int MaxLeftSum,MaxRightSum; //左半部分和右半部分的最大子序列和
int MaxLeftBorderSum,MaxRightBorderSum; //从中点向左右两边扫描的最大子序列和
int LeftBorderSum,RightBorderSum;
int Center,i;
if(Left == Right){ //递归的终止条件,子列只有一个数字
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 = LeftBorderSum = 0;
for(i = Center; i >= Left; i--){
LeftBorderSum += A[i];
if(LeftBorderSum > MaxLeftBorderSum)
MaxLeftBorderSum = LeftBorderSum;
}
MaxRightBorderSum = RightBorderSum = 0;
for(i = Center+1; i <= Right; i++){
RightBorderSum += A[i];
if(RightBorderSum > MaxRightBorderSum)
MaxRightBorderSum = RightBorderSum;
}
//MaxLeftBorderSum+MaxRightBorderSum就是跨分界线的最大子列和,然后三者比较
return Max3(MaxLeftSum,MaxRightSum,MaxLeftBorderSum+MaxRightBorderSum);
}