题目描述:连续子数组的最大和(连续最大公共子序列) 或许其实只有4个解法(看最后总结)
思路1:复杂度最高也是最直观的,求得所有的子序列,进行比较。时间复杂度O(n^3)
思路2:对第一个算法进行改进。有些重复计算的值,避免再次计算,因此时间复杂度O(n^2)
思路3:使用分治的思想,同时也使用了递归。时间复杂度O(n*logn)
思路4:动态规划,时间复杂度O(n)
思路5:叫不出名字的解法,有的称扫描法?而且时间复杂度也仅为O(n)
思路1实现代码:

public class Solution {

    public int FindGreatestSumOfSubArray(int[] array) {
        int maxSum = array[0];//如果设为0,在某些负数数组中会出错
        int sum;
     //最外层循环,每一层循环,都能拿到以当前角标i为数组的第一个元素时,所有子序列的最大值
     //如{1,-1,4,8}就分成{1,-1,4,8} {-1,4,8} {4,8} {8}四个部分分别求
        for (int i = 0; i < array.length; i++) {
     //第二层循环,以i=1为例,{-1,4,8} 由于j的递增,又分成了{-1,4,8}{4,8}{8} 三组 
            for (int j = i; j < array.length; j++) {
                sum = 0;
      //第三次循环,以i=1为例,j=1时,k的变化得到{-1} 结果 -1
      //第三次循环,以i=1为例,j=2时,k的变化得到{-1,4} 结果3   -1+4
      //第三次循环,以i=1为例,j=3时,k的变化得到{-1,4,8} 结果11   利用循环求和-1+4+8
                for (int k = i; k <= j; k++) {
                    sum += array[k];// 计算a[i]到a[j]的和
                }  
                if (sum > maxSum) {
                    maxSum = sum;
                }
            }
        }
        return maxSum;
    }
}

思路2实现:可以看到,{1,-1,4,8}在第以i=1为例,j=3时,第三轮得到{-1,4,8}的和的过程是通过一轮循环 -1+4+8得到,但是k变化的这一轮循环是完全可以避免的,可以用{-1,4}的结果加上8这样的方式得到。因为{-1,4}在j=2的时候已经计算过了,不需要再次循环从-1+4+8来一次循环,而是用之前计过的算结果就好了,因此可以降低复杂度,代码实现如下

public class Solution {

    public int FindGreatestSumOfSubArray(int[] array) {
        int maxSum = array[0];
        int sum;
    //最外层循环,如{1,-1,4,8}就分成{1,-1,4,8} {-1,4,8} {4,8} {8}四个部分分别求
        for (int i = 0; i < array.length; i++) {
            sum = 0;
    //以i=1为例 {-1,4,8} 在完成整个第二轮循环中,计算的结果是-1,3(-1+4),11(3+8)
    //第二种方法计算11的过程是没有用循环的,而是用上一个的结果3加8得到本次结果。
    //第一种方法得到11是-1+4+8,需要循环计算
            for (int j = i; j < array.length; j++) {
                sum += array[j];
                if (sum > maxSum) {
                    maxSum = sum;
                }
            }
        }
        return maxSum;
    }
}

思路3:分治--把问题分成两个大致相等的子问题,然后递归地对它们求解,这是“分”的部分。“治”阶段将两个子问题的解修补到一起,并可能再做些少量的附加工作,最后得到整个问题的解。
将数组分成两半后,问题的结果可能出现在左边,可能在右边,还可能左边有一部分元素,右边也有一点元素。
例如:D通过递归调用左B和右C,B告诉左最小,c告诉右最小,那么D本身还需要补一个跨中间的比较,然后三个值比较后return,返回的是D整体的最大值。在B中,通过递归调用E,F时,也会运行一个跨中间的比较,然后三值比较,进行return(那么D获得B整个部分的最大),在C通过递归调用G,H时,也会运行一个跨中间的比较,然后三值比较,进行return(那么D获得了C整个部分的最大)

本份代码调整了maxLeftBorderSum和maxRightBorderSum的赋初值的情况,可以进行所有元素是负数的情形,网上看了很多示例,都不能解决所有元素是负数的情况。另外边界条件也是分治(递归)值得注意的。

public class Solution {

   public int FindGreatestSumOfSubArray(int[] array) {
       return way(array,0,array.length-1);
    }
   public static int way(int [] arr,int left,int right) {
    //这是递归的返回情况,左右边界相等,直接返回
    if (left == right) {
       return arr[left];
    }

    //分割序列  分成两半
    int center = (left + right) / 2;
    //递归处理子序列    注意,递归返回的是子序列的最大值
    int maxLeftSum = way(arr, left, center);
    int maxRightSum = way(arr, center+1, right);

    //处理左序列  从中间边界向左边界计算
    //上面两行拿到了左和右两个部分最大值,现在处理合起来的部分   
    int maxLeftBorderSum = arr[center];   //左边界中,求和过程中的,带着center点的最大值
    int leftBorderSum = 0;      //左边界求和的值   
    //通过本轮循环,可以达到当前左边部分的最大值
    for (int i = center; i >= left; i--) {
        leftBorderSum += arr[i];
        if (leftBorderSum > maxLeftBorderSum) {
            maxLeftBorderSum = leftBorderSum;
        }
    }

    //处理右序列   从中间边界向后边界计算
    int maxRightBorderSum = arr[center+1];//右边界中,带着center+1点的最大值
    int rightBorderSum = 0;    //这是右边界所有的和
    for (int i = center+1; i <= right; i++) {
        rightBorderSum += arr[i];
        if (rightBorderSum > maxRightBorderSum) {
            maxRightBorderSum = rightBorderSum;
        }
    }
    //返回最大子序列和 maxLeftBorderSum+maxRightBorderSum是包含center中点的情况
        //所以是三个数,包含中间,左最大,右最大
    return Max(maxLeftBorderSum+maxRightBorderSum,maxLeftSum,maxRightSum);
}
    //三元运算求最值
    public static int Max(int a,int b, int c) {
    return a > b? (a > c ? a : c ): (b > c ? b : c);
}
}

第四种 动态规划(摘过来的答案,以后再来理解)
1、状态方程 : max(dp[i]) = getMax(max(dp[i-1]) + arr[i] ,arr[i])
2、上面式子的意义是:我们从头开始遍历数组,遍历到数组元素 arr[i] 时,连续的最大的和可能为
max(dp[i-1])+arr[i],也可能为arr[i] ,做比较即可得出哪个更大,取最大值。时间复杂度为O(n)。

public class Solution {
    public int FindGreatestSumOfSubArray(int[] array) {
        if(array.length == 0) return 0;
        int sum = array[0];
        int temp = array[0]; //注意初始值 不能设为0 防止只有负数
        for(int i = 1; i < array.length; i++) //从1开始 因为0的情况在初始化时完成了
        {
            temp = (temp < 0) ? array[i] : temp + array[i];
            sum = (temp > sum) ? temp : sum;
        }
        return sum;
    }
}

思路5:
我们可以从第一个数开始算起,每往后加一个数便更新一次和的最大值;当和成为负数时,则表明此前序列无法为后面提供最大子序列和,(不管后面是否正负,你这里为负数,加上你这个负数就更小了,还不如不加)因此必须重新确定序列首项,虽然重新确定序列首部,但是我已经用一个变量记录了之前的最大值了。

极端情况:全是负数时,temp变量一直是等于某个单个的元素,在移动,并与max比较,因此max通过比较也等于的是最小的负数

public class Solution {
    public int FindGreatestSumOfSubArray(int[] array) {
        int maxSum = array[0];  //设置为0,会导致有全负的数组的结果为0的错误
        int temp = 0;
          for( int i = 0; i < array.length ; i ++ ){
              temp += array[ i ];
              if(temp > maxSum)
                  maxSum = temp;
              else if(temp < 0)
                  temp = 0;
         }
         return maxSum;
    }
}

发现上下这两种解法其实是一个意思

public class Solution {
    public int FindGreatestSumOfSubArray(int[] array) {
       int temp=array[0];
        int maxSum=array[0];
        for(int i=1;i<array.length;i++){
            if(temp<0)   
                temp=array[i];
            else
                temp+=array[i];
            if(temp>maxSum)
                maxSum=temp;
        }
        return maxSum;
    }
}

总结,虽然动态规划的原理我现在暂时不懂,但是从代码上看,貌似摘过来的动态规划代码所描述的过程,和第五种思路的两种解法都非常相似。
无非就是(临时求和变量temp )temp小于0时,则temp应该重新赋值,而上面的写法有些是赋值0,有些是等于下个变量,其实是一个意思,因为你下一步总是要加下一个变量A,即temp=A,或者是temp=0再temp+=A,这两个写法上面都有,没区别。而如果temp大于0,就让temp加上下一个变量A,也就是temp=temp+A。再判断temp和当前记录的最大值maxsum,更新最大值。然后进入下一轮判断。三份代码的流程都是这样的,可能思路不同,但是写出代码的执行过程却非常相似,菜鸟表示很疑惑。
总之:由于复杂度是O(n),意味着每个元素只扫描了一次,需要保证扫描一次后,当前保留的maxsum是扫描过的最大值,所以意味着,最大子序列的头部在移动,并不是一直保持索引为0。所以扫描过程中需要跳过一些负面元素的影响,而temp就起到了这个所用。