题目链接:https://leetcode.com/problems/maximum-subarray/description/
题目大意:给出一串数组,找出其最大连续子序列和,例子如下:
法一(超时):时间复杂度o(n^2),两层for循环,记录每一个数值后的连续序列的最大和,里层for循环记录当前数值的连续序列的最大和,如果比最大值大,则记录。
这个方法可以用来解决记录最大序列和的开始和结束下标的问题,即在里层for循环中的当前数值的连续序列的最大和比最大值大时,出现调换时可以在这个步骤中记录开始和结束下标。
代码如下:
1 int res = Integer.MIN_VALUE; 2 for(int i = 0; i < nums.length; i++) { 3 int cnt = 0; 4 //对于每一个数值,判断其后面的连续子序列和得到最大值 5 for(int j = i; j < nums.length; j++) { 6 cnt += nums[j]; 7 if(cnt > res) { 8 res = cnt; 9 } 10 } 11 } 12 return res;
法二(借鉴):时间复杂度o(n),代码如下(耗时13ms):
1 //法一 2 /* 3 //这里要把res赋值为最小值,以防序列中负数的出现 4 int res = Integer.MIN_VALUE; 5 int sum = 0; 6 for(int i = 0; i < nums.length; i++) { 7 if(sum < 0) { 8 sum = nums[i];//如果当前值前面的sum<0,则将sum重新赋值为当前值,以此值为新的起点开始计算最大和 9 } 10 else { 11 sum += nums[i];//否则, 将当前值加入sum中 12 } 13 if(sum > res) { 14 res = sum; 15 } 16 } 17 return res;*/ 18 //法二 19 //结论:如果a[i]是负的,那么它不可能代表最优序列的起点,因为任何包含a[i]的作为起点的子序列都可以通过a[i+1]作起点而得到改进。类似地,任何负的子序列不可能是最优子序列的前缀 20 //如果在内循环中检测到从a[i]到a[j]的子序列是负的,那么可以推进i到j+1。 21 //在这里的运用也就是当sum<0时,即刻将sum重置为0。 22 //这里一定要把res赋值为最小值,因为序列中会有负数的情况 23 int res = Integer.MIN_VALUE; 24 int sum = 0; 25 for(int i = 0; i < nums.length; i++) { 26 sum += nums[i];//System.out.println(sum); 27 if(sum > res) { 28 res = sum; 29 } 30 //这里不要用else,因为类似序列{-2,-1}这种就计算不到最后的解 31 if(sum < 0) {//如果sum<0,则将sum重置,也就是发现sum<0,则说明此序列不会是最大和序列 32 sum = 0; 33 } 34 } 35 return res;
法三(借鉴):分治,时间复杂度o(nlogn),参考http://blog.csdn.net/silangquan/article/details/8062229, 代码如下(耗时18ms):
1 public int maxSubArray(int[] nums) { 2 return solution(nums, 0, nums.length - 1); 3 } 4 5 public int solution(int[] a, int left, int right) { 6 //处理left==right中间值情况 7 if(left == right) { 8 return a[left]; 9 } 10 11 int mid = (left + right) / 2; 12 //递归求左边最大和 13 int maxLeft = solution(a, left, mid); 14 //递归求右边最大和 15 int maxRight = solution(a, mid + 1, right); 16 17 //当前数组左边包含最后一个数的最大和 18 int maxLeftSum = Integer.MIN_VALUE; 19 int leftSum = 0; 20 for(int i = mid; i >= left; i--) { 21 leftSum += a[i]; 22 if(maxLeftSum < leftSum) { 23 maxLeftSum = leftSum; 24 } 25 } 26 27 //当前数组右边包含第一个数的最大和 28 int maxRightSum = Integer.MIN_VALUE; 29 int rightSum = 0; 30 for(int i = mid + 1; i <= right; i++) { 31 rightSum += a[i]; 32 if(maxRightSum < rightSum) { 33 maxRightSum = rightSum; 34 } 35 } 36 37 //左边最大和,右边最大和,包含左右两边的最大和,返回这三个值中的最大值 38 int tmp = maxLeft > maxRight ? maxLeft : maxRight; 39 return tmp > (maxLeftSum + maxRightSum) ? tmp : (maxLeftSum + maxRightSum); 40 }