LeetCode - Maximum Subarray

2 minute read

Problem description

description

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

Example:

1
2
3
Input: [-2,1,-3,4,-1,2,1,-5,4],
Output: 6
Explanation: [4,-1,2,1] has the largest sum = 6.

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.

Analysis

Basically idea is to use sum from index 0 to index j minus sum from index 0 to index i to get the sum of subarray from i to j.

Here’s the code:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
class Solution {
    public int maxSubArray(int[] nums) {
        int min = 0, max = Integer.MIN_VALUE;
        int sum = 0;
        int res = Integer.MIN_VALUE;
       
        int max_value = Integer.MIN_VALUE;
        
        if (nums.length == 1){
            return nums[0];
        }
        
        for (int i:nums){
            max_value = Math.max(max_value, i);
            sum += i;
            if (sum < min){
                min = sum;
                max = Integer.MIN_VALUE;
            }
            
            if (sum > max){
                max = sum;
            }
            
            if (max != Integer.MIN_VALUE){
                res = Math.max(res, max - min);                
            }
        }
        
        if (max_value < 0){
            return max_value;
        }
        
        return res;
    }
}

However, this solution is not straightforward and has many edge cases to deal with.

There’s a greedy solution from LeetCode, basic idea is to use the max current sum, use the nums[i] and sum from previous subarray + nums[i].

1
2
3
4
5
6
7
8
9
10
11
12
class Solution {
    public int maxSubArray(int[] nums) {
        int n = nums.length;
        int currSum = nums[0], maxSum = nums[0];

        for(int i = 1; i < n; ++i) {
        currSum = Math.max(nums[i], currSum + nums[i]);
        maxSum = Math.max(maxSum, currSum);
        }
        return maxSum;
    }
}

Also, LeetCode provide a divide and conquer method, which runs in O(NlogN), but the thought is great. The basic idea is to use something like merge sort.

here’s the code:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
class Solution {
  public int crossSum(int[] nums, int left, int right, int p) {
    if (left == right) return nums[left];

    int leftSubsum = Integer.MIN_VALUE;
    int currSum = 0;
    for(int i = p; i > left - 1; --i) {
      currSum += nums[i];
      leftSubsum = Math.max(leftSubsum, currSum);
    }

    int rightSubsum = Integer.MIN_VALUE;
    currSum = 0;
    for(int i = p + 1; i < right + 1; ++i) {
      currSum += nums[i];
      rightSubsum = Math.max(rightSubsum, currSum);
    }

    return leftSubsum + rightSubsum;
  }

  public int helper(int[] nums, int left, int right) {
    if (left == right) return nums[left];

    int p = (left + right) / 2;

    int leftSum = helper(nums, left, p);
    int rightSum = helper(nums, p + 1, right);
    int crossSum = crossSum(nums, left, right, p);

    return Math.max(Math.max(leftSum, rightSum), crossSum);
  }

  public int maxSubArray(int[] nums) {
    return helper(nums, 0, nums.length - 1);
  }
}

What to improve

  • main idea of this question is that if we have a smaller sum we should restart the subarray. Instead of using min, max to restart, a better idea is to just use the max between current sum and nums[i].

  • the edge case, only one item.

  • the merge sort like problem, if we found a problem that can be divide and conquer, use that.