问题:
给定N个整数的序列{A1,A2,…,AN,},求序列的最大子列和。
算法1
三重循环,时间复杂度O()。
int MaxSubseqSum1(int a[], int n)
{
int ThisSum, MaxSum = 0;
int i, j, k; // i是左端位置,j是右端位置
//* 三重循环
for (i = 0; i < n; i++)
{
for (j = 0; j < n; j++)
{
ThisSum = 0;
for (k = i; k <= j; k++)
{
ThisSum += a[k];
if (ThisSum > MaxSum)
MaxSum = ThisSum;
}
}
}
return MaxSum;
}
算法2
在算法1的基础上进行了改进,对于相同的i,不同的j,只要在j-1次循环的基础上累加一项即可
时间复杂度O()。
一个优秀的程序员看见时间复杂度为O()的算法,下意识会想办法将其优化成时间复杂度为O(nlogn)的算法。
int MaxSubseqSum2(int a[], int n)
{
int ThisSum, MaxSum = 0;
int i, j;
for (i = 0; i < n; i++)
{
ThisSum = 0;
//* 对于相同的i,不同的j,只要在j-1次循环的基础上累加一项即可
for (j = i; j < n; j++)
{
ThisSum += a[j];
if (ThisSum > MaxSum)
MaxSum = ThisSum;
}
}
return MaxSum;
}
算法3:分而治之
即将序列从中间分开,那么最大子列和出现在左侧、或右侧、或跨越边界。
左右两侧的最大子列和可以通过递归找出,跨越边界的最大子列和可以通过循环找出。
时间复杂度O()。
int MaxSubseqSum3(int a[], int left, int right) // left表示左边的位置,right表示右边的位置
{
int MaxLeftSum, MaxRightSum;
int LeftSum, RightSum;
int MaxLeftBorderSum = 0, MaxRightBorderSum = 0; //存放跨分界线的结果
int LeftBorderSum = 0, RightBorderSum = 0;
int center, i;
// 递归结束的条件,子列一个元素时(每一次递归中left和right都是在变化的)
if (left == right)
{
if (a[left] > 0)
{
return a[left];
}
else
return 0;
}
//下面是"分"的过程
center = (left + right) / 2; //找到中分点
//递归求得两边子列的最大和
MaxLeftSum = MaxSubseqSum3(a, left, center);
MaxRightSum = MaxSubseqSum3(a, center + 1, right);
//下面求跨分界线的最大子列和
for (i = center; i >= left; i--) //从中线向左扫描
{
LeftBorderSum += a[i];
if (LeftBorderSum > MaxLeftBorderSum)
{
MaxLeftBorderSum = LeftBorderSum;
}
} //左边扫描结束
for (i = center + 1; i <= right; i++) //从中线向右扫描
{
RightBorderSum += a[i];
if (RightBorderSum > MaxRightBorderSum)
{
MaxRightBorderSum = RightBorderSum;
}
} //右边扫描结束
//下面返回"治"的结果
return MaxOfThreeNumbers(MaxLeftSum, MaxRightSum, MaxLeftBorderSum + MaxRightBorderSum);
}
//返回3个整数中的最大值
int MaxOfThreeNumbers(int a, int b, int c)
{
int maxnum;
maxnum = a > b ? a : b;
maxnum = maxnum > c ? maxnum : c;
return maxnum;
}
算法4:在线处理
"在线"的意思是指每输入一个数据就进行及时处理,在任何一个地方中止输入,算法都能得到正确的当前解。
定义ThisSum和MaxSum分别存储当前子列和和最大子列和,从左到右遍历序列,ThisSum存储经过序列的和,
当序列和小于0时,说明这段序列不会使序列和更大,舍弃这一段序列,当其大于MaxSum时则更新MaxSum的值。
时间复杂度为O(n)。
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;
}