问题:输入是具有n个浮点数的向量x,输出是输入向量的任何连续子向量中的最大和,认为当所有的输入都是负数时,总和最大的子向量是空向量,总和为0。例如,如果输入向量包含下面10个元素:
那么该程序的输出为x[2..6]的总和,即187。
书中主要提到了3中不同的算法,其时间复杂度也各不相同。
1.平方算法
主要是通过遍历数组中数来计算出连续子向量的最大和,时间复杂度为O(n^2)。
平方算法的伪代码如下:
maxsofar=0
for i=[0,n)
sum=0
for j=[i,n)
sum+=x[j]
maxsofar=max(maxsofar, sum)
2.分治算法
该算法主要是基于如下的分治原理:要解决规模为n的问题,可递归地解决两个规模近似为n/2的子问题,然后对它们的答案进行合并以得到整个问题的答案。
在本例中,初始问题要处理大小为n的向量,所以将它划分为两个子问题的最自然方法就是创建两个大小近似相等的子向量,分别成为a和b。
然后递归地找出a,b中元素总和最大的子向量,分别称为ma和mb。
最大子向量要么整个在a中,要么整个在b中,要么跨越a和b之间的边界。我们将跨越边界的最大子向量称为mc。
分治算法将递归地计算ma和mb,并通过其他某种方法计算mc,然后返回3个总和中的最大者。
为了计算mc,通过观察可以发现,mc在a中的部分是a中包含右边界的最大子向量,而mc在b中的部分是b中包含左边界的最大子向量。
将这些因素综合到一起就得到了下面的算法伪代码:
float maxsum(l,u)
if(l>u)
return 0
if(l==u)
return max(x[l], 0)
m=(l+u)/2
// find max across to left
lmax=sum=0
for(i=m;i>=l;i--)
sum+=x[i]
lmax=max(lmax, sum)
// find max across to right
rmax=sum=0
for i=(m,u]
sum+=x[i]
rmax=max(rmax, sum)
return max(maxsum(l,m), maxsum(m+1,u), lmax+rmax)
算法的最初调用:maxsum(0,n-1)
时间复杂度为 O(nlogn),有多种方法可以证明其运行时间。1,一种非正式的论证是,该算法在每层递归中都执行了O(n)次操作,而总计有O(logn)层递归。
2,更精确的论证可以通过递归关系完成。若用T(n)表示解决规模为n的问题所需的时间,那么T(1)=O(1)且T(n)=2T(n/2)+O(n) 可知该递推关系的解为 T(n)=O(nlogn)。
3.扫描算法
现在采用操作数组的最简单的算法:从数组最左端(元素x[0])开始扫描,一直到最右端(元素x[n-1])为止,并记下所遇到的最大总和子向量。最大总和的初始值设为0。假设我们已解决了x[0..i-1]的问题,那么如何将其扩展为包含x[i]的问题呢?我们使用类似于分治算法的原理:前i个元素中,最大总和子数组要么在前i-1个元素中(将其存储在maxsofar中),要么其结束位置为i(将其存储在maxendinghere中)。
算法伪代码如下:
maxsofar=0
maxendinghere=0
for i=[0,n)
maxendinghere=max(maxendinghere+x[i], 0)
maxsofar=max(maxendinghere, maxsofar)
算法的时间复杂度为
O(n)。