#初学算法#分治策略---最大子数列的暴力求解和分治求解对比

最近学了分治策略,基本步骤为:

  1. 分解:将问题分解为一些子问题,子问题和原问题形式一样,只是规模更小;
  2. 解决:求解出子问题,当规模足够小时,则停止递归,直接求解;
  3. 合并:将足问题的解合成原问题的解。

求最大子数组(mid为n/2),则最大子数组有三种情况:
  • 全部位于mid左边;
  • 全部位于mid右边;
  • 经过mid的子数组。
暴力求解
即按顺序,从左至右进行求最大和,i=0 to n-1and j=i to n-1 ,比较求得最大值,那么算法复杂度很明显是O(n^2);这里不多说。

分治求解
  1. 分解:mid=n/2;原问题分解成求mid左边最大子数组和求mid右边最大子数组;
  2. 解决:分解的同时已求得左右最大子数组,再去求经过mid的最大子数组,最后比较三种情况的最大值;
  3. 合并:从递归到规模最小,逐层合并求得最大子数组。
下面上代码:
求解工具类:
public class ArrayUtil {
	public static int actionCount=0; //计算运算次数,以度量算法复杂度
	/*
	 * 暴力求解
	 */
	public static MaxArray getMaxArrayInForce(int[] arr){
		int first=0;
		int last=0;
		actionCount=0;
		if(arr.length==0)
			return null;
		MaxArray maxArray = new MaxArray();
		int maxSum=arr[0];
		
		for(int i=0;i<arr.length;i++){   //开始暴力求解
			int sum=0;
			for(int j=i;j<arr.length;j++){
				sum+=arr[j];
				actionCount++;
				if(maxSum<sum){
					maxSum=sum;
					first=i;
					last=j;
				}
					
			}
		}
		int[] resultArr=new int[last-first+1];
		int cur=0;
		for(int i = first;i<=last;i++){   //数组赋值
			resultArr[cur++]=arr[i];
			actionCount++;
		}
		maxArray.setSum(maxSum);
		maxArray.setFirst(first);
		maxArray.setLast(last);
		maxArray.setArray(resultArr);
		return maxArray;
	}
	/*
	 * 分治策略求解
	 */
	public static MaxArray getMaxArrayInDivide(int[] arr) {
		if(arr.length==0)
			return null;
		ArrayResult arrayResult=getMaxSub(arr,0,arr.length-1);
		MaxArray maxArray=new MaxArray();
		maxArray.setSum(arrayResult.sum);
		maxArray.setFirst(arrayResult.low);
		maxArray.setLast(arrayResult.high);
		int[] myArr=new int[maxArray.last-maxArray.first+1];
		for(int i=maxArray.first;i<=maxArray.last;i++){
			myArr[i-maxArray.first]=arr[i];
			actionCount++;
		}
		maxArray.setArray(myArr);
		return maxArray;
	}
	/*
	 * 递归求解最大子数组
	 */
	public static ArrayResult getMaxSub(int[] array ,int low,int high ){
		
		if(high==low){   //递归的最小规模
			ArrayResult result=new ArrayResult();
			result.high=high;
			result.low=low;
			result.sum=array[low];
			return result;
		}
		else{
			int mid=(high+low)/2;
			ArrayResult leftResult=getMaxSub(array, low, mid);      //左递归
			ArrayResult rightResult=getMaxSub(array, mid+1, high);  //右递归
			ArrayResult crossResult=getMaxCrossSub(array, low, high, mid);  //求中间
			if(leftResult.sum>=rightResult.sum&&leftResult.sum>=crossResult.sum)
				return leftResult;
			else if(rightResult.sum>=leftResult.sum&&rightResult.sum>=crossResult.sum)
				return rightResult;
			else
				return crossResult;	
		}
	}
	/*
	 * 求经过中间的最大子数组
	 */
	private static ArrayResult getMaxCrossSub(int[] array ,int low,int high,int mid) {
		int leftSum=array[mid];
		int sum=0;
		ArrayResult result=new ArrayResult();
		for(int i=mid;i>=low;i--){      //求经过mid左边的最大子数组
			actionCount++;
			sum+=array[i];
			if(sum>leftSum){
				leftSum=sum;
				result.low=i;
			}
		}
		int rightSum=array[mid+1];
		sum=0;
		for(int j=mid+1;j<=high;j++){    //求经过mid右边的最大子数组
			actionCount++;
			sum+=array[j];
			if(sum>rightSum){
				rightSum=sum;
				result.high=j;
			}
		}
		result.sum=rightSum+leftSum;    //合并左右
		return result;
	}
	public static class ArrayResult{    //要用static类,因为static函数不能调用动态类(第88行)
		int low=0;                      //而内部类用于将返回值打包起来
		int high=0;
		int sum=0;
	}

}

存储类:

public class MaxArray {
	
	int first=0;
	int last=0;
	int sum;
	int[] array=null;
	/**
	 * @return the first
	 */
	public int getFirst() {
		return first;
	}
	/**
	 * @param first the first to set
	 */
	public void setFirst(int first) {
		this.first = first;
	}
	/**
	 * @return the last
	 */
	public int getLast() {
		return last;
	}
	/**
	 * @param last the last to set
	 */
	public void setLast(int last) {
		this.last = last;
	}
	/**
	 * @return the array
	 */
	public int[] getArray() {
		return array;
	}
	/**
	 * @param array the array to set
	 */
	public void setArray(int[] array) {
		this.array = array;
	}
	/**
	 * @return the sum
	 */
	public int getSum() {
		return sum;
	}
	/**
	 * @param sum the sum to set
	 */
	public void setSum(int sum) {
		this.sum = sum;
	}
	
}

下面是测试类:

public class Test {
	public static void main(String[] args) {
		int[] testArr={27,45,1,-5,-5,7,-4,-3,8,-15,19,-12,-14};
		//MaxArray maxArray=ArrayUtil.getMaxArrayInForce(testArr);  //暴力求解
		MaxArray maxArray=ArrayUtil.getMaxArrayInDivide(testArr);   //分治策略求解
		int[] resultArr=maxArray.getArray();
		System.out.println("最大子数组为第"+maxArray.first+1+"至第"+(maxArray.last+1)+"个数的数组");
		System.out.println("最大子数组为:");
		for(int i=0;i<resultArr.length;i++){
			System.out.print(resultArr[i]+"  ");
		}
		System.out.println();
		System.out.println("最大子数组的和为:"+maxArray.sum);
		System.out.println("操作次数:"+ArrayUtil.actionCount);
	}

}

测试结果:

暴力求解:

分治求解:



完毕!

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值