编程珠玑。关于寻找最大相加连续自串
输入 31 -41 59 26 -53 58 97 -93 -23 84
输出187
原因 59 26 -53 58 97相加的和为187
算法分治算法。
分治算法并不稀奇,快速排序,归并排序就是一个典型的分治算法。
分治算法总是和递归有着离不开的关系。分治算法最简单的问题已经是一个可直接求解的问题。比如排序分治到最后就剩一个数,那么一个数一定是已经排序好的。快速排序的一个改进方法就是在分治到子串大小为32左右的时候就调用插入排序算法。因为插入排序算法在32个数一下的时候比快速排序要快一点。
分治算法的编程格式
return_T fun( args....)
1确定递归结束条件,如子串个数为零,左索引大于右索引等等
2本子串的过程,如划分字串等等。。这里要注意划分分割点的归属问题,只能在一个子串中。如源码所示
3进行分治。对子串调用递归
package test;
public class test
{
public static int findMax(int[]arr,int l,int r)
{
if(l>r) return 0;
if(l==r) return arr[l]>0?arr[l]:0;
int m=(l+r)/2;
int lmax=0;
int sum=0;
for(int i=m;i>=l;i--)
{
sum+=arr[i];
if(sum>lmax)
{
lmax=sum;
}
}
int rmax=0;
sum=0;
for(int i=m+1;i<=r;i++)//这里一定要是m+1,因为我们已经把m放到前一个子串中
{
sum+=arr[i];
if(sum>rmax)
{
rmax=sum;
}
}
int sidemax=Math.max(findMax(arr, l,m), findMax(arr,m+1/*<span style="font-family: Arial, Helvetica, sans-serif;">这里一定要是m+1,因为我们已经把m放到前一个子串中*/</span>,r));
int middlemax=rmax+lmax;
return Math.max(sidemax, middlemax);
}
public static void main(String []args)
{
int arr[]={31,-41,59,26,-53,58,97,-93,-23,84};
//int arr[]={59,26,-53,58,97};
System.out.println(findMax(arr, 0, arr.length-1));
}
};
这个算法的执行效率是nlog(n)。。
还有一个类似于动态规划算法。这个算法可以在线性时间内解决这个问题
假设我们已经得出了[0 i-1]的解maxsofar.如何把这个解拓展到[0 i]呢?
1在[0 i-1]中带有i-1元素的最长子串.可能是1到i-1 也可能是 2到 i-1 也可能 什么都没有,那就是0.我们定义为maxendinghere。
[0 i]的maxendinghere=[0 i-1]的maxendinghere +arr[i] 和0 的中大者。
可以想象,如果i-1的maxendinghere +arr[i]是正的,那么说明前面的子串(包括x[i])还有可能成为最大子串,说白了,还可能对后来的构建最大子串有贡献。
如果是负的,说明前面子串(包括x[i])已经是不可能是最大子串了。因为他是负的,如果子串不截断,那么他会使子串的值减小。最好就是一刀切断,以绝后患。新的子串重新开始构造
2[0 i]的最优解只可能从两个地方产生,a原来的最优解。bmaxendinghere +arr[i]。
给出伪代码
maxsofar=0
maxendinghere=0
for i=[0,n)//这里没有n
maxendinghere=max (maxdinghere+x[i],0)
maxsofar=max(maxsofar,maxendinghere)