最近學了分治策略,基本步驟爲:
- 分解:將問題分解爲一些子問題,子問題和原問題形式一樣,只是規模更小;
- 解決:求解出子問題,當規模足夠小時,則停止遞歸,直接求解;
- 合併:將足問題的解合成原問題的解。
求最大子數組(mid爲n/2),則最大子數組有三種情況:
- 全部位於mid左邊;
- 全部位於mid右邊;
- 經過mid的子數組。
暴力求解:即按順序,從左至右進行求最大和,i=0 to n-1and j=i to n-1 ,比較求得最大值,那麼算法複雜度很明顯是O(n^2);這裏不多說。分治求解:
- 分解:mid=n/2;原問題分解成求mid左邊最大子數組和求mid右邊最大子數組;
- 解決:分解的同時已求得左右最大子數組,再去求經過mid的最大子數組,最後比較三種情況的最大值;
- 合併:從遞歸到規模最小,逐層合併求得最大子數組。
下面上代碼:求解工具類:
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);
}
}
測試結果:
暴力求解:
分治求解:
完畢!