最大子列和问题
给定N个整数的序列{A1,A2,A3,…,AN},求函数的最大值.
最直接最暴力的算法:把所有的子列和都算出来,找出最大的那一个.
int MaxSubseqSum1( int A[], int N )
{ int ThisSum, MaxSum = 0;
int i, j, k;
for( i = 0; i < N; i++ ) { /* i是子列左端位置 */
for( j = i; j < N; j++ ) { /* j是子列右端位置 */
ThisSum = 0; /* ThisSum是从A[i]到A[j]的子列和 */
for( k = i; k <= j; k++ )
ThisSum += A[k];
if( ThisSum > MaxSum ) /* 如果刚得到的这个子列和更大 */
MaxSum = ThisSum; /* 则更新结果 */
} /* j循环结束 */
} /* i循环结束 */
return MaxSum;
}
这个算法非常的不聪明.
当我们知道一个从i到j的和的时候,我们计算下一个从i到j,并不需要从头开始加.当j增加了1的时候,我们只需要在前面的i到j的部分和后面加一个元素就好了,k循环是多余的.
算法2
int MaxSubseqSum2( int A[], int N )
{ int ThisSum, MaxSum = 0;
int i, j;
for( i = 0; i < N; i++ ) { /* i是子列左端位置 */
ThisSum = 0; /* ThisSum是从A[i]到A[j]的子列和 */
for( j = i; j < N; j++ ) { /* j是子列右端位置 */
ThisSum += A[j]; /*对于相同的i,不同的j,只要在j-1次循环的基础上累加1项即可*/
if( ThisSum > MaxSum ) /* 如果刚得到的这个子列和更大 */
MaxSum = ThisSum; /* 则更新结果 */
} /* j循环结束 */
} /* i循环结束 */
return MaxSum;
}
这个算法的复杂度为T(N)=O(N2).两重循环的嵌套.
但这个算法并不是最好的.
当我们发现一个算法的复杂度为O(N2)时,我们应该下意识的想到能否将它改进为O(NlogN).
算法3:分而治之
把一个大的问题切分成很多块,再分头去解决每个小的问题,最后再把结果合并起来
分:把这个数组从中间一分为二
治:然后递归地去解决左右两边的问题,得到左边的最大子列和和右边的最大子列和以及跨越边界的最大子列和,比较三者最大值即为所求的最大子数列
int MaxSubSeqSum(int arr[], int left, int right) {
if(left == right){
if(arr[left] > 0){
return arr[left];
}else {
return 0;
}
}
int center = (left + right) / 2;
int leftMaxSum = MaxSubSeqSum(arr, left, center); /* 分界线左侧最大子数列 */
int rigthMaxSum = MaxSubSeqSum(arr, center+1, right); /* 分界线右侧最大子数列 */
/* 以分界线往左求最大子数列 */
int leftBorderSum = 0, maxLeftBorderSum = 0;
for(int i=center; i>=left; i--) {
leftBorderSum += arr[i];
if(leftBorderSum > maxLeftBorderSum){
maxLeftBorderSum = leftBorderSum;
}
}
/* 以分界线往右求最大子数列 */
int rightBorderSum = 0, maxRightBorderSum = 0;
for(int j=center+1; j<=right; j++) {
rightBorderSum += arr[j];
if(rightBorderSum > maxRightBorderSum) {
maxRightBorderSum = rightBorderSum;
}
}
/* 跨越分界线最大子数列和 */
int maxBorderSum = maxLeftBorderSum + maxRightBorderSum;
return maxSum(leftMaxSum, rigthMaxSum, maxBorderSum);
}
/*
** 返回a, b, c三者最大值
*/
int maxSum(int a, int b, int c){
if(a > b) {
a = b;
}
if(a > c) {
return a;
}else {
return c;
}
}
T(N) = 2 *T(N/2) +cN T(1)=O(1)
= 2[2 * T(N/22) +cN/2]+cN
=2kO(1)+ck N 其中N/2k=1
= O(NlogN)
但这仍然不是最快的算法.
算法4:在线处理(每输入一个数据就进行即时处理)
int MaxSubseqSum4( int A[], int N )
{ int ThisSum, MaxSum;
int i;
ThisSum = MaxSum = 0;
for( i = 0; i < N; i++ ) {
ThisSum += A[i]; /* 向右累加 */
if( ThisSum > MaxSum )
MaxSum = ThisSum; /* 发现更大和则更新当前结果 */
else if( ThisSum < 0 ) /* 如果当前子列和为负 */
ThisSum = 0; /* 则不可能使后面的部分和增大,抛弃之 */
}
return MaxSum;
}
它里面只有一个for循环,所以它的时间复杂度为T(N)=O(N),是线性的.
副作用:它的正确性不是特别的明显.