53. Maximum Subarray

作者: ShutLove | 来源:发表于2017-09-03 14:03 被阅读55次

    题目描述:
    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.
    即找出一个给定数组中加和最大的连续子数组。

    第一个直观解法,就是把每个元素作为子数组的起始元素,找出和最大的子数组,伪代码:

        int max = Integer.MIN_VALUE;
        for (int i = 0; i < nums.length; i++) {
            int tmpSum = 0;
            for (int j = i; j < nums.length; j++) {
                tmpSum += nums[j];
                if (tmpSum > max) {
                    max = tmpSum;
                }
            }
        }
    

    这里可以做一些优化,就是定义一个前缀和数组int[] sum,这样求i到j的和就变为了sum[j] - sum[i],避免了每次都要重复的加运算,当然这个解法时间复杂度还是n方会超时。

    第二种解法:动态规划。
    每个元素都会是n个子数组的末尾元素,那么这些子数组中肯定有一个是有最大和的。如果知道了以第n个元素为末尾的最大子数组和,求第n+1就很好知道了。以sum[n]表示第n个元素为末尾的最大子数组和,如果sum[n]大于等于0,那么sum[n+1] = sum[n] + num[n+1];否则sum[n+1] = num[n+1]。

    public int maxSubArray1(int[] nums) {
        if (nums == null || nums.length == 0) {
            return 0;
        }
    
        int n = nums.length;
        int[] dp = new int[n];
        dp[0] = nums[0];
        int max = nums[0];
        for (int i = 1; i < n; i++) {
            dp[i] = nums[i] + (dp[i-1] > 0 ? dp[i-1] : 0);
            max = Math.max(max, dp[i]);
        }
    
        return max;
    }
    

    可以观察到求dp[i]的时候只与dp[i-1]有关,所以这里空间上可以再压缩,dp只需要声明为大小为两个元素的数组。

    public int maxSubArray2(int[] nums) {
        if (nums == null || nums.length == 0) {
            return 0;
        }
    
        int n = nums.length;
        int[] dp = new int[2];
        dp[0] = nums[0];
        int max = nums[0];
        for (int i = 1; i < n; i++) {
            dp[i%2] = nums[i] + (dp[(i-1)%2] > 0 ? dp[(i-1)%2] : 0);
            max = Math.max(max, dp[i%2]);
        }
    
        return max;
    }
    

    但是leetcode上这种写法比前一种要慢,可能是取模运算的问题吧。

    看这道题目的标签还有分治方法,自己没想出来,翻看discuss找到了。这种解法的思路是:找到数组的middle元素,那么这个middle元素可能在最大子数组中,也可能不在;如果不在,那么最大子数组要么在middle左侧,要么在middle右侧;最后求左侧、右侧、和包含middle的最大数组三者的最大值就是结果了。

    public int maxSubArray3(int[] nums) {
        if (nums == null || nums.length == 0) {
            return 0;
        }
    
        return divideConquer(nums, 0, nums.length - 1);
    }
    
    private int divideConquer(int[] nums, int left, int right) {
        if (left == right) {
            return nums[left];
        }
    
        int middle = left + (right - left) / 2;
        int leftRes = divideConquer(nums, left, middle);
        int rightRes = divideConquer(nums, middle + 1, right);
    
        int leftMax = nums[middle];
        int rightMax = nums[middle + 1];
        int tmp = 0;
        for (int i = middle; i >= left; i--) {
            tmp += nums[i];
            leftMax = Math.max(leftMax, tmp);
        }
        tmp = 0;
        for (int i = middle + 1; i <= right; i++) {
            tmp += nums[i];
            rightMax = Math.max(rightMax, tmp);
        }
    
        return Math.max(leftMax + rightMax, Math.max(leftRes, rightRes));
    }

    相关文章

      网友评论

        本文标题:53. Maximum Subarray

        本文链接:https://www.haomeiwen.com/subject/pkxjjxtx.html