#初學算法#分治策略---最大子數列的暴力求解和分治求解對比

最近學了分治策略,基本步驟爲:

  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);
	}

}

測試結果:

暴力求解:

分治求解:



完畢!


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章