- 问题描述
最大子序列问题是一个常见的数据结构问题,即求出给定数组{1,3,4,5,6-10,3}
中加和最大的子序列,这个例子的解很明显是{1,3,4,5,6}。
- 解决算法分析
-这个问题一看便有一个明显的思路,即穷举法,利用双层循环来得到所有子序列的加和大小,然后取出最大的即为解。
java代码:
public int findMax(int[] a){
int max=0;
for(int i=0;i<a.length;i++){
int tempMax=0;
for(int j=i;j<a.length;j++){
tempMax+=a[j];
if(tempMax>max){
max= tempMax;
}
}
}
return max;
}
这个是最简单也是很多人想到的第一算法,他的时间复杂度明显是O(n2),
这里我主要想介绍的是另一种递归的解决方法,主要利用了分治算法的思想,先看代码:
//分治算法
public int findMax(int[] a,int left,int right){
int result=0;
//基准条件 既退出条件
if(left==right){
result=a[left]>0?a[left]:0;
return result;
}
//重复调用
int centerIndex=(left+right)/2;
int maxLeft=findMax1(a, left, centerIndex);
int maxRight=findMax1(a, centerIndex+1, right);
//分治算法
int maxBorderLeft=0,leftBorderSum=0;
for(int i=centerIndex;i>=left;i--){
leftBorderSum+=a[i];
if(leftBorderSum>maxBorderLeft){
maxBorderLeft=leftBorderSum;
}
}
int maxBordeRight=0,rightBorderSum=0;
for(int i=centerIndex+1;i<=right;i++){
rightBorderSum+=a[i];
if(rightBorderSum>maxBordeRight){
maxBordeRight=rightBorderSum;
}
}
//返回最大值
result=maxBordeRight+maxBorderLeft;
if(result<maxLeft){
result=maxLeft;
}
if(result<maxRight){
result=maxRight;
}
return result;
}
@Test
public void test(){
int[] x={1,2,3,4,-10,6};
System.err.println(this.findMax1(x,0,x.length-1));
}
这个代码可以按注释分为几块,递归算法第一步要设置算法的退出条件,也就是base case,这里的base case是这个数组只有一个元素的时候,那么直接退出就可以了,如果不是的话要用分治思想来求解:
1,把数组分成两个部分,分别求出最大的和, 然后从中间向两遍查找,求出最大的和,三个部分最大的数即为本问题的解。这里读者最好画图来帮助理解,这个是这个算法的关键。 时间复杂度是O(nlogn)
最后介绍一种这个问题的最优解,
public int findMax(int[] a){
int result=0,sum=0;
for(int i=0;i<a.length;i++){
sum+=a[i];
if(sum>result){
result = sum ;
}else if(sum<0){
sum=0;
}
}
return result;
}
这个的时间复杂度降到了O(n), 优秀的算法总是很难懂,但是我们可以拿上面的例子一一带入来看看为什么这个算法成立,才能理解这种写法
例子{1,2,4-10,4}
第一次执行:result=1, sum 1
第二次执行:result=3, sum 3
第三次执行:result=7, sum 7
第四次执行:result=7, sum 0
第五次执行:result=7, sum 4
结果是7