Maximum Subarray

来源:互联网 发布:苹果手机价格评估软件 编辑:程序博客网 时间:2024/05/01 18:50

Find the contiguous subarray within an array (containing at least one number) which has the largest sum.

For example, given the array [−2,1,−3,4,−1,2,1,−5,4],
the contiguous subarray [4,−1,2,1] has the largest sum = 6.

click to show more practice.

More practice:

If you have figured out the O(n) solution, try coding another solution using the divide and conquer approach, which is more subtle.

解法3: http://blog.csdn.net/linhuanmars/article/details/21314059

这是一道非常经典的动态规划的题目,用到的思路我们在别的动态规划题目中也很常用,以后我们称为”局部最优和全局最优解法“。
基本思路是这样的,在每一步,我们维护两个变量,一个是全局最优,就是到当前元素为止最优的解是,一个是局部最优,就是必须包含当前元素的最优的解。接下来说说动态规划的递推式(这是动态规划最重要的步骤,递归式出来了,基本上代码框架也就出来了)。假设我们已知第i步的global[i](全局最优)和local[i](局部最优),那么第i+1步的表达式是:
local[i+1]=Math.max(A[i], local[i]+A[i]),就是局部最优是一定要包含当前元素,所以不然就是上一步的局部最优local[i]+当前元素A[i](因为local[i]一定包含第i个元素,所以不违反条件),但是如果local[i]是负的,那么加上他就不如不需要的,所以不然就是直接用A[i];
global[i+1]=Math(local[i+1],global[i]),有了当前一步的局部最优,那么全局最优就是当前的局部最优或者还是原来的全局最优(所有情况都会被涵盖进来,因为最优的解如果不包含当前元素,那么前面会被维护在全局最优里面,如果包含当前元素,那么就是这个局部最优)。

接下来我们分析一下复杂度,时间上只需要扫描一次数组,所以时间复杂度是O(n)。空间上我们可以看出表达式中只需要用到上一步local[i]和global[i]就可以得到下一步的结果,所以我们在实现中可以用一个变量来迭代这个结果,不需要是一个数组,也就是如程序中实现的那样,所以空间复杂度是两个变量(local和global),即O(2)=O(1)。

public class Solution {    public int maxSubArray(int[] A) {        if(A.length == 0 || A == null) return 0;       int global = A[0];       int local = A[0];              for(int i=1; i<A.length; i++){           local = Math.max(local+A[i],A[i]);           global = Math.max(global,local);       }       return global;    }}

解法1:遍历,sum+A[i]>0 就往前走,加,但是<0,说明是负数,没必要继续了。但是max已经记录了最大值。

public class Solution {    public int maxSubArray(int[] A) {        int sum = 0;        int max = Integer.MIN_VALUE;                for(int i= 0; i<A.length; i++){           sum +=A[i];           if(sum>max){               max = sum;           }           if(sum<0){               sum = 0;           }        }        return max;    }}

Divide and conquer approach;

recursive.

思路:三种情况,区间落在左边,落在右边,或者包含中间。

左边,右边的recursive,最后还是要利用中间的算。

中间的情况:

算 mid -> left, max value. 

算 mid-> right , max value.

然后算三种情况哪个最大,就是要返回的值 Math.max(Math.max(leftmax, rightmax), midleftmax + midrightmax + A[mid])


注意:算左边的时候,必须倒着算:

for(int i = mid-1; i>=start;i--){

因为要算consecutive的区间,必须从中间往左边算。这样才能和右边的连接起来。

不然这样就不好了:|__left max___| ----- mid |_right max___|  这样就连接不起来了。


public class Solution {    public int maxSubArray(int[] A) {       int start = 0;        int end = A.length-1;       int maxvalue = Integer.MIN_VALUE;       return findMaxValue(A,start,end,maxvalue);    }        public int findMaxValue(int[] A, int start, int end, int maxvalue){        if(start>end){            return Integer.MIN_VALUE;        }        int mid = start + (end-start)/2;        int leftmax = findMaxValue(A,start,mid-1,maxvalue);        int rightmax = findMaxValue(A,mid+1,end,maxvalue);        maxvalue = Math.max(Math.max(leftmax,rightmax),maxvalue);                //calculate cross mid situtation;        // mid -> left;         int midleftsum = 0;          int midleftmax = 0;         for(int i = mid-1; i>=start;i--){             midleftsum += A[i];             if(midleftsum>midleftmax){                 midleftmax = midleftsum;             }         }                  //mid -> right;         int midrightsum = 0;         int midrightmax = 0;         for(int j=mid+1; j<=end; j++){             midrightsum +=A[j];             if(midrightsum > midrightmax){                 midrightmax = midrightsum;             }         }                  maxvalue = Math.max(maxvalue, midleftmax + midrightmax + A[mid]);         return maxvalue;    }}





0 0