求解最大子數組
問題描述,神馬是最大子數組
假定我們一個已知的數組爲:
int numbers[]={1,2,6,3,-5,-7,1,-6,8};
現在我們需要求解其中某幾個數之和最大的一段子數組,如果不考慮方法的複雜性,首先我們可以考慮暴力求解,即將所有可能的組合進行排列組合。那麼我們可以得到n*(n-1)/2(不考慮順序的排列組合)種可能,其複雜度爲O(n^2)。
爲求得更好的解題思路,我們可以考慮使用分治法。
首先我們將數組按照中點分成兩個部分
這樣求解最大子數組的問題就變成了求左邊部分,右邊部分,跨過中間部分的最大子數組,其中求解左邊和右邊與求解整個數組的最大子數組邏輯相同。關鍵在於如何求解中間部分的最大子數組。對於求解中間部分的最大子數組,由於該數組會經過中點,所以可以分解爲兩部分,分別爲從middle向左出發,到左邊某個index的最大值以及從middle出發,到右邊某個index的最大值。將兩個部分的最大值相加即可得到跨過中點的最大子數組,其時間複雜度爲O(n);其基本實現如下:
public static Model findCrossMiddle(int leftIndex,int middle,int rightIndex){ int leftSum=Integer.MIN_VALUE; int leftRes=middle; int rightSum=Integer.MIN_VALUE; int rightRes=middle+1; int sum=0; for(int i=middle;i>=leftIndex;i--){ sum+=numbers[i]; if(sum>leftSum){ leftSum=sum; leftRes=i; } } //從middle向左出發,到左邊某個index的最大值,並記錄下標 sum=0; for(int j=middle+1;j<=rightIndex;j++){ sum+=numbers[j]; if(sum>rightSum){ rightSum=sum; rightRes=j; } } //從middle向左出發,到左邊某個index的最大值,並記錄下標 return new Model(leftRes,rightRes,(leftSum+rightSum)); }
實現算法
分析完基本實現思路,很自然就想到通過遞歸完成算法,要想通過遞歸,就要先知道算法的結束條件。在這裏就是當劃分的子數組只有一個元素時,則直接返回。在每次遞歸的時候,比較左中右三段中的哪個最大子數組最大,並返回表示該段中的最大子數組。
其完整實現代碼如下:
public class MaxChildArray { private static int numbers[]={1,2,6,3,1,8}; public static void main(String[] args) { Model res=findMaxChildArray(0,numbers.length-1); System.out.println(res); } //查找最大子數組的遞歸方法 public static Model findMaxChildArray(int leftIndex,int rightIndex){ if(leftIndex==rightIndex){ return new Model(leftIndex,rightIndex,numbers[leftIndex]); } else{ int middle=((leftIndex+rightIndex)/2); Model modelLeft=findMaxChildArray(leftIndex,middle); Model modelRight=findMaxChildArray(middle+1, rightIndex); Model acrossModel=findCrossMiddle(leftIndex,middle,rightIndex); if(modelLeft.getSum()>=modelRight.getSum()&&modelLeft.getSum()>=acrossModel.getSum()){ return modelLeft; } else if(modelRight.getSum()>=modelLeft.getSum()&&modelRight.getSum()>=acrossModel.getSum()){ return modelRight; } else{ return acrossModel; } } } //查找經過中點的最大子數組 public static Model findCrossMiddle(int leftIndex,int middle,int rightIndex){ int leftSum=Integer.MIN_VALUE; int leftRes=middle; int rightSum=Integer.MIN_VALUE; int rightRes=middle+1; int sum=0; for(int i=middle;i>=leftIndex;i--){ sum+=numbers[i]; if(sum>leftSum){ leftSum=sum; leftRes=i; } } sum=0; for(int j=middle+1;j<=rightIndex;j++){ sum+=numbers[j]; if(sum>rightSum){ rightSum=sum; rightRes=j; } } return new Model(leftRes,rightRes,(leftSum+rightSum)); } //定義數據模型 public static class Model{ private int leftIndex; private int rightIndex; private int sum; public Model(int leftIndex, int rightIndex, int sum) { super(); this.leftIndex = leftIndex; this.rightIndex = rightIndex; this.sum = sum; } public int getLeftIndex() { return leftIndex; } public void setLeftIndex(int leftIndex) { this.leftIndex = leftIndex; } public int getRightIndex() { return rightIndex; } public void setRightIndex(int rightIndex) { this.rightIndex = rightIndex; } public int getSum() { return sum; } public void setSum(int sum) { this.sum = sum; } @Override public String toString() { return "Model [leftIndex=" + leftIndex + ", rightIndex=" + rightIndex + ", sum=" + sum + "]"; } } }