一口气读了好几章《编程珠玑》!阅读的速度是不是有点快?!不管了,作者深邃的思想不是我一时半会能全部参悟透的,现在先将能理解的理解掉,像这样经典的计算机书籍是值得反复阅读的,今后再更深层次地去理解作者的思想,现在先解决具体的问题:已知一个一维数组,求该数组中任意连续元素构成的最大和(即求最大连续子数组的和)。很容易想到用穷举法来解这道题,时间复杂度为O(n*n),我吃惊于竟然存在复杂度为O(n)的解法。下面就依次给出时间复杂度为O(n*n)、O(nlogn)、O(n)的穷举法、递归法和扫描法。
/* 方法一:穷举法
* 利用两层for循环,
* 第一层for循环是列举数组中的所有元素分别作为连续子数组的第一个元素时的情况,
* 第二层for循环是在连续子数组的第一个元素确定的情况下,寻找最大的和。
*/
#include <iostream>
using namespace std;
int main()
{
int n=10; //n表示数组元素的个数
int a[10]= {31,-41,59,26,-53,58,97,-93,-23,84}; //a为给出的一维数组
int max=0; //max为所求最大连续子数组的值
for(int i=0; i<n; i++)
{
int sum=0;
for(int j=i; j<n; j++)
{
sum+=a[j];
if(sum>max) max=sum;
}
}
cout <<max<< endl;
return 0;
}
/* 方法二:递归法
* 递归算法就不阐述了,值得注意的是合并左右数组的解时,
* 需要考虑横跨两个集合的最大连续子数组的情况
*/
#include <iostream>
using namespace std;
int main()
{
int n=10; //n表示数组元素的个数
int a[10]= {31,-41,59,26,-53,58,97,-93,-23,84}; //a为给出的一维数组
int max(int,int,int); //返回三者中的最大数
int maxSum(int [],int,int); //递归函数
cout<<maxSum(a,0,n-1)<<endl;
return 0;
}
int max(int a,int b,int c)
{
int d=a>b?a:b;
return d>c?d:c;
}
int maxSum(int a[],int l,int u)//a为数组,l、u分别为数组的下界和上届
{
if(l>u) return 0; //当l>u时,空子数组的和最大,为0
if(l==u) return a[l]>0?a[l]:0;//当l==u时,数组只有一个元素,则最大子数组要么是它本身,要么为空子数组
int m=(l+u)/2;
int lMax=0;
int sum=0;
for(int i=m; i>=l; i--)//求横跨两个集合的最大连续子数组的左侧最大和
{
sum+=a[i];
if(sum>lMax) lMax=sum;
}
int rMax=0;
sum=0;
for(int i=m+1; i<=u; i++) //求横跨两个集合的最大连续子数组的右侧最大和
{
sum+=a[i];
if(sum>rMax) rMax=sum;
}
return max(maxSum(a,l,m),maxSum(a,m+1,u),lMax+rMax); //递归调用
}
/* 方法三:扫描法
* 扫描法用到一个思想:若最大连续子数组包括一个负数,
* 则这个负数加上与这个负数同一侧的所有数字的结果一定是一个正数。
*/
#include <iostream>
using namespace std;
int main()
{
int n=10; //n表示数组元素的个数
int a[10]= {31,-41,59,26,-53,58,97,-93,-23,84}; //a为给出的一维数组
int max(int,int);//max为一个函数,返回两者中的较大值
int maxSofar=0; //maxSofar记录到目前为止出现过的最大和
int maxEndHere=0;//maxEndHere记录最大连续子数组的左侧的和
for(int i=0; i<n; i++)
{
maxEndHere=max(maxEndHere+a[i],0);//与0求较大者,确保构成最大连续子数组的左侧的和一定是个正数
maxSofar=max(maxSofar,maxEndHere);
}
cout<<maxSofar<<endl;
return 0;
}
int max(int a,int b)
{
return a>b?a:b;
}