leetcode: Maximum Subarray

问题描述:

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.

原问题链接:https://leetcode.com/problems/maximum-subarray/

问题分析

线性时间解法 

  这个问题在之前的文章中有过详细的分析,它有一个比较时间复杂度为O(N)的高效率实现。基本思路如下,定义一个记录当前连续子串和的变量curMax。它每次去和我们需要返回的最终值max比较,去最大的值赋给max。而当判断到curMax < 0的时候,则重置curMax为0。具体的实现代码如下:

public class Solution {
    public int maxSubArray(int[] nums) {
        int max = nums[0], curMax = 0;
        for(int i = 0; i < nums.length; i++) {
            curMax += nums[i];
            max = Math.max(curMax, max);
            if(curMax < 0) curMax = 0;
        }
        return max;
    }
}

分治法

 在很多地方分治法都得到广泛的应用,比如说归并排序里也是这样的思路。我们将一个问题划分成更小的问题,然后通过这些小的问题合并起来得到最终的结果。所以按照这个思路,我们可以这样来解决问题:

  针对给定的一个区段,首先将它划分成相等的两个部分。这个最大连续子串可能出现在它的左边子串里,也可能出现在它的右边子串里,也有可能是通过跨越左右两个子串的这个串。所以我们要计算出它们两个部分,去它们中间最大的那个。这部分流程概括起来如下:

  findMax(num, l, r) = max(findMax(num, l, mid), findMax(num, mid + 1, r), findMaxCrossArray(num, l, mid, r)) 。其中mid是取l, r的中间值。findMaxCrossArray则表示从中间值mid开始向两边扩展得到的那个涵盖两个子串的最大连续子串。针对这个递归的函数,它还有一个返回的条件就是l == r,这是作为递归划分的一个终止条件。

  最终得到的详细代码实现如下:

public class Solution {
    public int maxSubArray(int[] nums) {
        return findMax(nums, 0, nums.length - 1);
    }
    
    public int findMax(int[] num, int l, int r) {
        if(l == r) return num[l];
        int mid = (l + r) / 2;
        int left = findMax(num, l, mid);
        int right = findMax(num, mid + 1, r);
        int cross = findMaxCrossSubArray(num, l, mid, r);
        return Math.max(left, Math.max(right, cross));
    }
    
    public int findMaxCrossSubArray(int[] num, int l, int mid, int r) {
        int leftSum = num[mid], sum = 0;
        for(int i = mid; i >= l; i--) {
            sum += num[i];
            leftSum = Math.max(sum, leftSum);
        }
        int rightSum = num[mid + 1];
        sum = 0;
        for(int j = mid + 1; j <= r; j++) {
            sum += num[j];
            rightSum = Math.max(sum, rightSum);
        }
        return leftSum + rightSum;
    }
}

  这个方法的实现效率为O(NlogN)。因为这里有递归的过程,它的递归调用栈也要占用logN的空间。所以效率相对不是很高。不过这种思路值得好好的学习借鉴。

参考材料

Introduction to algorithms 

猜你喜欢

转载自shmilyaw-hotmail-com.iteye.com/blog/2293394