Maximum Subarray LT53
Given an integer array nums
, find the contiguous subarray (containing at least one number) which has the largest sum and return its sum.
Example:
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.
Idea 1: For all pairs of integers i and j satisfying 0 <= i <= j < nums.length, check whether the sum of nums[i..j] is greater than the maximum sum so far, take advange of:
sum of nums[i..j] = sum of nums[i..j-1] + nums[j]
the sum of all continuous subarray starting at i can be calculated in O(n), hence we have a quadratic algorithm.
Time complexity: O(n2)
Space complexity: O(1)
class Solution { public int maxSubArray(int[] nums) { int sz = nums.length; int maxSumSoFar = Integer.MIN_VALUE; for(int i = 0; i < sz; ++i) { int sumStartHere = 0; for(int j = i; j < sz; ++j) { sumStartHere += nums[j]; maxSumSoFar = Math.max(maxSumSoFar, sumStartHere); } } return maxSumSoFar; } }
Idea 1.a: With the help of a cumulative sum array, cumarr[0...i], which can be computed in linear time, it allows the sum to be computed quickly,
sum[i..j] = cumarr[j] - cumarr[i-1].
Time complexity: O(n2)
Space complexity: O(n)
class Solution { public int maxSubArray(int[] nums) { if(nums == null || nums.length < 1) return 0; int sz = nums.length; int[] cumuSum = new int[sz]; cumuSum[0] = nums[0]; for(int i = 1; i < sz; ++i) { cumuSum[i] = cumuSum[i-1] + nums[i]; } int maxSumSoFar = Integer.MIN_VALUE; for(int i = 0; i < sz; ++i) { for(int j = i; j < sz; ++j) { int previousSum = 0; if(i > 0) { previousSum = cumuSum[i-1]; } maxSumSoFar = Math.max(maxSumSoFar, cumuSum[j] - previousSum); } } return maxSumSoFar; } }
class Solution { public int maxSubArray(int[] nums) { if(nums == null || nums.length < 1) return 0; int sz = nums.length; int[] cumuSum = new int[sz]; cumuSum[0] = nums[0]; for(int i = 1; i < sz; ++i) { cumuSum[i] = cumuSum[i-1] + nums[i]; } int maxSumSoFar = Integer.MIN_VALUE; for(int j = 0; j < sz; ++j) { maxSumSoFar = Math.max(maxSumSoFar, cumuSum[j]); for(int i = 1; i <= j; ++i) { maxSumSoFar = Math.max(maxSumSoFar, cumuSum[j] - cumuSum[i-1]); } } return maxSumSoFar; } }
Idea 2: divide and conquer. Divide into two subproblems, recusively find the maximum in subvectors(max[i..k], max[k..j]) and find the maximum of crossing subvectors(max[i..k..j]), return the max of max[i..k], max[k..j] and max[i..k..j].
Time complexity: O(nlgn)
Space complexity: O(lgn) the stack
class Solution { private int maxSubArrayHelper(int[] nums, int l, int u) { if(l >= u) return Integer.MIN_VALUE; int mid = l + (u - l)/2; int leftMaxSum = nums[mid]; int sum = 0; for(int left = mid; left >=l; --left) { sum += nums[left]; leftMaxSum = Math.max(leftMaxSum, sum); } int rightMaxSum = 0; sum = 0; for(int right = mid+1; right < u; ++right) { sum += nums[right]; rightMaxSum = Math.max(rightMaxSum, sum); } return Math.max(leftMaxSum + rightMaxSum, Math.max(maxSubArrayHelper(nums, l, mid), maxSubArrayHelper(nums, mid+1, u))); } public int maxSubArray(int[] nums) { return maxSubArrayHelper(nums, 0, nums.length); } }
Idea 3: Extend the solution to the next element in the array. How can we extend a solution for nums[0...i-1] to nums[0..i].
The key is the max sum ended in each element, if extending to the next element,
maxHere(i) = Math.max( maxHere(i-1) + nums[i], nums[i])
maxSoFar = Math.max(maxSoFar, maxHere)
Time compleixty: O(n)
Space complexity: O(1)
class Solution { public int maxSubArray(int[] nums) { int maxHere = 0; int maxSoFar = Integer.MIN_VALUE; for(int num: nums) { maxHere = Math.max(maxHere, 0) + num; maxSoFar = Math.max(maxSoFar, maxHere); } return maxSoFar; } }
Idea 3.a: Use the cumulative sum,
maxHere = cumuSum(i) - minCumuSum
cumuSum(i) = cumuSum(i-1) + nums[i]
maxSoFar = Math.max(maxSoFar, maxHere) = Math.max(maxSoFar, cumuSum - minCumuSum)
Time compleixty: O(n)
Space complexity: O(1)
class Solution { public int maxSubArray(int[] nums) { int min = 0; int cumuSum = 0; int maxSoFar = Integer.MIN_VALUE; for(int num: nums) { cumuSum += num; maxSoFar = Math.max(maxSoFar, cumuSum - min); min = Math.min(min, cumuSum); } return maxSoFar; } }