问题描述

给定整数 ,求的最大值。约定当所有整数都为负数时,最大子序列的和为0。

解决思路分析

思路一

将整个序列的所有子序列遍历一遍,求这些所有子序列中的最大值。 java代码如下:

	  public static int maxSubSum1(int data[]) {
	    int maxSum = 0;
	    for (int i = 0; i < data.length; i++)
	      for (int j = i; j < data.length; j++) {
	        int tempSum = 0;
	        for (int k = i; k <= j; k++) {
	          tempSum += data[k];
	        }
	        if (tempSum > maxSum) {
	          maxSum = tempSum;
	        }
	      }
	    return maxSum;
	  }

时间复杂度:O(n^3)</br> 空间复杂度:O(1)

思路二

对思路一尽行优化,即在遍历所有子序列时向后遍历,即可省去第3层循环 java代码如下:

	  public static int maxSubSum2(int data[]) {
	    int maxSum = 0;
	    for (int i = 0; i < data.length; i++) {
	      int tempSum = 0;
	      for (int j = i; j < data.length; j++) {
	        tempSum += data[j];
	        if (tempSum > maxSum) {
	          maxSum = tempSum;
	        }
	      }
	    }
	    return maxSum;
	  }

时间复杂度:O(n^2)</br> 空间复杂度:O(1)

思路三

使用二分分治的思想,即将整个序列分为前后两部分,那么该最大子序列可能出现在前半部分,也可能出现在后半部分,也有可能同时包含了两个部分(此情况下必定包含前半部分的最后一个元素,也必定包含后半部分的第一个元素),按照此思路我们可以设计一个递归函数,一直二分下去,在上述3种情况下求最大值即为最大子序列和。</br>

java代码如下:

	  private static int maxSumRec(int[] data, int head, int end) {
	    if (head == end) {
	      if (data[head] > 0) {
	        return data[head];
	      } else {
	        return 0;
	      }
	    }
	
	    int middle = (head + end) / 2;
	    int maxHeadSum = maxSumRec(data, head, middle);
	    int maxEndSum = maxSumRec(data, middle + 1, end);
	
	    int maxHeadBorderSum = 0;
	    int tempHeadBorderSum = 0;
	    for (int i = middle; i >= head; i--) {
	      tempHeadBorderSum += data[i];
	      if (tempHeadBorderSum > maxHeadBorderSum) {
	        maxHeadBorderSum = tempHeadBorderSum;
	      }
	    }
	
	    int maxEndBorderSum = 0;
	    int tempEndBorderSum = 0;
	    for (int i = middle + 1; i <= end; i++) {
	      tempEndBorderSum += data[i];
	      if (tempEndBorderSum > maxEndBorderSum) {
	        maxEndBorderSum = tempEndBorderSum;
	      }
	    }
	
	    int maxBorderSum = maxHeadBorderSum + maxEndBorderSum;
	    int temp = 0;
	    return (temp = (maxEndSum > maxHeadSum ? maxEndSum : maxHeadSum)) > maxBorderSum ? temp
	        : maxBorderSum;
	  }

	  public static int maxSubSum3(int data[]) {
    	return maxSumRec(data, 0, data.length - 1);
  	  }

时间复杂度:O(nlogn)</br> 空间复杂度:O(1)

思路四

最为精巧的算法实现,即当一个从头开始遍历的子序列和为负数时,忽略掉这个子序列中的所有元素,再从剩下的元素中去求最大序列和。 java代码如下:

	  public static int maxSubSum4(int data[]) {
	    int maxSum = 0;
	    int tempSum = 0;
	
	    for (int i = 0; i < data.length; i++) {
	      tempSum += data[i];
	      if (tempSum < 0) {
	        tempSum = 0;
	      }
	      if (tempSum > maxSum) {
	        maxSum = tempSum;
	      }
	    }
	
	    return maxSum;
	  }

时间复杂度:O(n)</br> 空间复杂度:O(1)

结论

在这四个实现中,我们发现了一个问题,即实现起来简单易懂的算法往往效果并不太好,而那些所谓精妙的算法,虽然相对难以理解一些,但往往可以取得非常好的效果。此外,当我们一时想不到这些精妙的算法时,折衷的做法是利用分治的思想,因为这个思想是“万金油”,到哪儿都能较好的解决问题,相对也容易理解一些。