最大子列和问题:给定N个整数的序列,求如下函数的最大值:
算法1:
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; // 则更新结果
}
return MaxSum;
}
时间复杂度为O(N³)。
算法2:
int MaxSubseqSum2(int A[], int N){
int ThisSum, MaxSum = 0;
int i, j;
for (i = 0; i < N; i++){ // i为子列左端位置
ThisSum = 0; // A[i]到A[j]的子列和
for (j = i; j < N; j++){ // j为子列右端位置
ThisSum += A[j]; // 对于相同的i,不同的j,只要在j-1次循环的基础上累加一项即可
if (ThisSum > MaxSum)
MaxSum = ThisSum;
}
}
return MaxSum;
}
时间复杂度为O(N²)。
算法3:分而治之
int Max3(int A, int B, int C){ //返回三个整数中的最大值
return A > B ? A > C ? A : C : B > C ? B : C;
}
int DivideAndConquer(int List[], int left, int right) {
//分治法求List[left]到List[right]的最大子列和
int MaxLeftSum, MaxRightSum; //存放左右子问题的解
int MaxLeftBorderSum, MaxRightBorderSum; //存放跨分界线的结果
int LeftBorderSum, RightBorderSum;
int center, i;
if (left == right) { //递归终止条件,子列只有一个数字
if (List[left] > 0)
return List[left];
else return 0;
}
//下面是"分"的过程
center = (left + right) / 2; //找到中分点
//递归求两边子列的最大和
MaxLeftSum = DivideAndConquer(List, left, center);
MaxRightSum = DivideAndConquer(List, center + 1, right);
//下面求跨分界线的最大子列和
MaxLeftBorderSum = 0; LeftBorderSum = 0;
for (i = center; i >= left; i--) { //从中线向左扫描
LeftBorderSum += List[i];
if (LeftBorderSum > MaxLeftBorderSum)
MaxLeftBorderSum = LeftBorderSum;
} //左边扫描结束
MaxRightBorderSum = 0; RightBorderSum = 0;
for (i = center + 1; i <= right; i++) { //从中线向右扫描
RightBorderSum += List[i];
if (RightBorderSum > MaxRightBorderSum)
MaxRightBorderSum = RightBorderSum;
} //右边扫描结束
//下面返回治的结果
return Max3(MaxLeftSum, MaxRightSum, MaxLeftBorderSum + MaxRightBorderSum);
}
int MaxSubseqSum3(int List[], int N){ //保持与前2种算法相同的函数接口
return DivideAndConquer(List,0,N-1);
}
时间复杂度为O(NlogN)。推导:设处理整条序列的时间复杂度为T(N),则左子问题和右子问题的复杂度均为T(N/2),跨分界线情况复杂度为O(N)。于是有:T(N)=2T(N/2)+cN=2[2T(N/2²)+cN/2]+cN=2^kO(1)+ckN=O(Nlog N),其中k满足N/2^k=1。
算法4:在线处理
int MaxSubseqSum4(int A[], int N){
int i;
int ThisSum =0, MaxSum = 0;
for (i = 0; i < N; i++) {
ThisSum += A[i]; //向右累加
if (ThisSum > MaxSum)
MaxSum = ThisSum; //发现更大和则更新当前结果
else if (ThisSum < 0) //如果当前子列和为负
ThisSum = 0; //不可能使后面的部分和增大,抛弃
}
return MaxSum;
}
时间复杂度仅为O(N)。
Tips:1. 一个for循环的时间复杂度等于循环次数乘以循环体代码的复杂度;
2. if-else结构的复杂度取决于if的条件判断复杂度和两个分支部分的复杂度,总体复杂度取三者中最大。