【LeetCode】No.53. Maximum Subarray -- Java Version

69 阅读2分钟

携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第2天。

题目链接: leetcode.com/problems/ma…

1. 题目介绍(Maximum Subarray)

Given an integer array nums, find the contiguous subarray (containing at least one number) which has the largest sum and return its sum.

【Translate】: 给定一个整数数组nums,找到具有最大和的连续子数组(至少包含一个数字)并返回其和。

A subarray is a contiguous part of an array.

【Translate】: 子数组是数组的连续部分。

【测试用例】:

testcase

【约束】:

Constraints

Follow up: If you have figured out the O(n) solution, try coding another solution using the divide and conquer approach, which is more subtle.

【Translate】: 如果你已经想出了O(n)的解决方案,尝试使用分治方法编写另一个解决方案,这是更微妙的。

2. 题解

2.1 穷举 -- O(n^2^)

  该题很容易就能想到穷举,简单易行,依次遍历所有可能,找出连续相加和中最大的数,但就是时间复杂度太高了。

class Solution {
    public int maxSubArray(int[] nums) {
        int n = nums.length;
        int maxSum = Integer.MIN_VALUE;
        for (int i = 0; i < n; i++)
        {
            int sum = 0;
            for (int j = i; j < n; j++)
            {
                sum = sum + nums[j];
                if (sum > maxSum)
                {
                    maxSum = sum;
                }
            }
        }
        return maxSum;
    }
}

case1

2.2 一次遍历 -- O(n)

  原题解来自于Chaitanya1706的JAVA | Kadane's Algorithm | Explanation Using Image。该题解的思想如下图所示,顺序遍历所有元素,通过sum记录逐渐相加的和,通过max记录当前最大值。这里值得注意的一点是,如果在任意一点sum变为负数,那么就没有必要再保留它了,因为0显然大于负,所以求和为0(不要怕会改变最后结果,因为下一次循环,sum又被赋予了新值)。 case2mind

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

case2

2.3 分治 -- O(N\log{N})

Divide-and-conquer consider 3 cases:

  • Case 1: Subarray in the left half -> leftSum
  • Case 2: Subarray in the right half -> rightSum
  • Case 3: Subarray crosses the middle -> crossSum

原题解来自于junhaowanggg的Easy-Understand Java Solutions with Explanations (B-F, Divide-And-Conquer, DP)

class Solution {
    public int maxSubArray(int[] nums) {
      int n = nums.length;
      return maxSubArray(nums, 0, n - 1);
    }

    private int maxSubArray(int[] nums, int lo, int hi) {
      if (lo == hi) { // base case: one number
        return nums[lo];
      }
      // divide
      int mid = lo + (hi - lo) / 2;
      // conquer
      int leftSum = maxSubArray(nums, lo, mid);
      int rightSum = maxSubArray(nums, mid + 1, hi);
      // combine
      int crossSum = crossSum(nums, lo, hi);
      return Math.max(crossSum, Math.max(leftSum, rightSum));
    }

    // invariant: lo < hi (left part and right part both have at least 1 element
    private int crossSum(int[] nums, int lo, int hi) {
      int mid = lo + (hi - lo) / 2;
      // left
      int leftSum = 0, leftMax = Integer.MIN_VALUE; // the invariant means that leftMax and rightMax will be updated
      for (int i = mid; i >= lo; --i) {
        leftSum += nums[i];
        leftMax = Math.max(leftMax, leftSum);
      }
      // right
      int rightSum = 0, rightMax = Integer.MIN_VALUE;
      for (int i = mid + 1; i <= hi; ++i) {
        rightSum += nums[i];
        rightMax = Math.max(rightMax, rightSum);
      }
      return leftMax + rightMax;
    }
}

case3