――设计优秀的算法
(From Programming Peals by Jon Bentley)
一个简单的问题,往往有许多不同的解决方案,那么如何找到一个相比之下比较好(事实上很难找到绝对好的方法)的方案呢?我们需要一个优秀的算法,优秀的算法能够给很大程度上提高程序的效率。
问题描述:问题的输入是具有n个整数的向量x,输出是输入向量的任何连续子向量中的最大和。例如,如果输入向量包含下面10个元素:
31 ,-41, 59, 26 ,-53, 58 , 97,-93,-23 , 84
当所有的输入都是正数的时候,问题很容易解决,此时最大的子向量就是整个输入向量。
但是,如果出现负数该怎么办,这就是我们下面所研究的几个算法所讨论的内容。
一个简单平方算法(O(n2)):
int maxsofar=0;
for(int i=0;i<n;i++)
{
sum=0 ;
for(int j=I;j<n;j++)
{
sum+=x[j];
maxsofar= max( maxsofar,sum );
}
}
一个分治算法(O(log n)):
分治原理:
要解决规模为n的问题,可递归地解决两个规模近似为n/2的子问题,然后对他们的答案进行合并以得到整个问题的答案。
程序略
一个现在看来最高效的程序——scan算法(O(n)):
下面给出此程序的C++完整代码:
#include <iostream.h>
//#include <stdlib.h>
int max(int a,int b)
{
return a>b?a:b;
}
int scan(int a[],int n)
{
int maxsofar,maxendinghere;
maxsofar=maxendinghere=0;
for(int i=0;i<n;i++)
{
maxendinghere=max(maxendinghere+a[i],0);
maxsofar=max(maxsofar,maxendinghere);
}
return maxsofar;
}
void main()
{
int a[10]={-6,-8,-89,7,3,8,-79,45,8,-5};
cout<<scan(a,10)<<endl;
}
可以看出scan算法是一个接近完美的算法,因为解决这个问题少于O(n)是不可能的。
附上结构之法算法之道blog的解法:
#include <iostream>
using namespace std;
//结构之法算法之道blog的算法
/*-------------------------------------
解释下:
例如输入的数组为1, -2, 3, 10, -4, 7, 2, -5,
那么最大的子数组为3, 10, -4, 7, 2,
因此输出为该子数组的和18。
所有的东西都在以下俩行,
即:
b : 0 1 -1 3 13 9 16 18 13
sum: 0 1 1 3 13 13 16 18 18
其实算法很简单,当前面的几个数,加起来后,b<0后,
把b重新赋值,置为下一个元素,b=a[i]。
当b>sum,则更新sum=b;
若b<sum,则sum保持原值,不更新。。July、10/31。
此法和编程珠玑中方法本质是一样的
----------------------------------*/
int maxSum(int* a, int n)
{
int sum=0;
//其实要处理全是负数的情况,很简单,如稍后下面第3点所见,直接把这句改成:"int sum=a[0]"即可
//也可以不改,当全是负数的情况,直接返回0,也不见得不行。
int b=0;
for(int i=0; i<n; i++)
{
if(b<0) //...
b=a[i];
else
b+=a[i];
if(sum<b)
sum=b;
}
return sum;
}