[编程珠玑]-第八章:连续子向量的最大和

源于编程珠玑-第二版

问题描述:

一维模式识别,输入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

第一层循环执行n次,第二层循环最多执行n次,总时间O(n^2)


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;i
   
   
    
    sum?max:sum;
			}
		}
		System.out.println(max);
	}
	public static void FindMax(int A[]){
		//O(n)
		int max=0,tempmax=0;
		for(int i=0;i
    
    
     
     0?(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.下界



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值