源于编程珠玑-第二版
问题描述:
一维模式识别,输入n个数值向量x,输出连续子向量的最大和。
题目很简单,即求最大子数组。
1.平方算法:O(n^2)
需要对所有连续组合进行对比,选择出累积和最大的值,算法的优化在于减少反复计算对比。
对于X[i...j]的总和,与前X[i...j-1]的总和密切相关, 省去反复计算i~j的值
max=0
for i=[0,n)
sum=0
for j=[i,n)
sum+=X[j]
max=max>sum?max:sum
2.扫描算法:O(n)
从数组最左端开始扫描,直到最右端,记下所遇到的总和最大值max(初始为0)和当前所得扫描值tempmax。
在前i个元素中,最大总和子数组存在于前i-1个元素中,或结束于i
在每推移一个元素时,都已记录下此前最大总和max,因此即便此时当前元素为负值,也无妨,
下一刻tempmax依然会与max相对比,直到一次阶段结束(tempmax<0时),将tempmax复位成0继续后续扫描。
max=0
tempmax=0
for i =[0,n)
tempmax=(tempmax+X[i])>0?(tempmax+X[i]):0
max=max>tempmax?max:tempmax
public static void FindMax2(int A[]){
//O(n^2)
int max=0,sum=0;
for(int i=0;isum?max:sum;
}
}
System.out.println(max);
}
public static void FindMax(int A[]){
//O(n)
int max=0,tempmax=0;
for(int i=0;i0?(tempmax+A[i]):0;
max=max>=tempmax?max:tempmax;
}
System.out.println(max);
}
3.分治算法
将规模为n的问题划分为两个近似n/2的子问题递归解决。
将长度为n的向量均分为向量a b
递归查找a b中元素总和最大的子向量 ma mb
最大的子向量总和在于 a 中或 b中 或 横跨 a、b界限(一部分在a,一部分在b)
需要对比这3者,取其中最大值。
在算法导论中有较为详细地介绍了以递归分治来计算最大子数组的案例。
例如 A[]={1,4,-5,3,2} ,则下标0~1 0~4 3~4 均可得到总和5 。
由于原始算法只输出其中的一种情况,我稍微修改了一下。输出所有可能情况
(但实际上不够优化,反而会加大了运行时间,这里只为了输出多种情况而测试,可删去和list相关的代码)
public static ArrayList myList=new ArrayList<>();
public static int[] FindMaxMumSubArray(int A[],int low,int high) {
if(low==high){
int a[]={low,high,A[low]};
myList.add(a);
return a;
}
else{
int mid=(low+high)/2;
int left[]=FindMaxMumSubArray(A, low, mid);
int right[]=FindMaxMumSubArray(A, mid+1,high);
int cross[]=FindMaxCrossingSubArray(A,low, mid,high);
if(left[2]>right[2]&&left[2]>cross[2]){
myList.clear();
myList.add(left);
return left;
}
else if (right[2]>left[2]&&right[2]>cross[2]){
myList.clear();
myList.add(right);
return right;
}
else {
myList.clear();
myList.add(cross);
if(left[2]==cross[2])
myList.add(left);
if(right[2]==cross[2])
myList.add(right);
return cross;
}
}
}
private static int[] FindMaxCrossingSubArray(int[] A, int low, int mid, int high) {
int max_left=0,left_sum=Integer.MIN_VALUE;
int sum=0;
for (int i = mid; i >= low; i--) {
sum+=A[i];
if(sum>left_sum){
left_sum=sum;
max_left=i;
}
}
int max_right=0,right_sum=Integer.MIN_VALUE;
sum=0;
for (int i = mid+1; i <=high; i++) {
sum+=A[i];
if(sum>right_sum){
right_sum=sum;
max_right=i;
}
}
int a[]={max_left,max_right,left_sum+right_sum};
return a;
}
int A[]={1,4,-5,3,2};
int a[]=FindMaxMumSubArray(A, 0, A.length-1);
System.out.println(Arrays.toString(a));
for (Object o:myList) {
int list[]=(int[])o;
System.out.println(Arrays.toString(list));
}
——————————————————————
《编程珠玑》本章提到的算法注意:
1.保存状态,避免重复计算
2.将信息预处理到数据结构中
3.分治算法
4.扫描算法,通过存储已有答案和辅助数据计算
5.累加数组,第i元素等于前i-1个元素的总和,所需某一数值时可通过相减获得
6.下界