求解最大子数组
问题描述,神马是最大子数组
假定我们一个已知的数组为:
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 + "]"; } } }