Given a circular array C of integers represented by A
, find the maximum possible sum of a non-empty subarray of C.
Here, a circular array means the end of the array connects to the beginning of the array. (Formally, C[i] = A[i]
when 0 <= i < A.length
, and C[i+A.length] = C[i]
when i >= 0
.)
Also, a subarray may only include each element of the fixed buffer A
at most once. (Formally, for a subarray C[i], C[i+1], ..., C[j]
, there does not exist i <= k1, k2 <= j
with k1 % A.length = k2 % A.length
.)
Example 1:
Input: [1,-2,3,-2]
Output: 3
Explanation: Subarray [3] has maximum sum 3
Example 2:
Input: [5,-3,5]
Output: 10
Explanation: Subarray [5,5] has maximum sum 5 + 5 = 10
Example 3:
Input: [3,-1,2,-1]
Output: 4
Explanation: Subarray [2,-1,3] has maximum sum 2 + (-1) + 3 = 4
Example 4:
Input: [3,-2,2,-3]
Output: 3
Explanation: Subarray [3] and [3,-2,2] both have maximum sum 3
Example 5:
Input: [-2,-3,-1]
Output: -1
Explanation: Subarray [-1] has maximum sum -1
Idea 1. Similar to Maximum Subarray LT53, the difference is the circular array part, the subarray could be the tail(A[j..n-1]) plus the head(A[0..i]) (i+1 < j), if the subarray of tail + head is the maximum, the subarray in the middle is A[i+1, j-1] has the minSum, once the minSum is found, the maxSum is Sum - minSum, except maxSum = 0 if all values in the array are negative, as minSum == Sum.
Note. negative arrays
Time complexity: O(n)
Space complexity: O(1)
1 class Solution { 2 public int maxSubarraySumCircular(int[] A) { 3 int maxSoFar = 0; 4 int minSoFar = 0; 5 6 int sum = 0; 7 int minSum = Integer.MAX_VALUE; 8 int maxSum = Integer.MIN_VALUE; 9 10 for(int a: A) { 11 maxSoFar = a + Math.max(maxSoFar, 0); 12 maxSum = Math.max(maxSum, maxSoFar); 13 14 minSoFar = a + Math.min(minSoFar, 0); 15 minSum = Math.min(minSum, minSoFar); 16 17 sum += a; 18 } 19 20 if(sum == minSum) { 21 return maxSum; 22 } 23 return Math.max(maxSum, sum - minSum); 24 } 25 }
Idea 2. 另外一种方法求2个intervals (head + tail)组成的,如果直接从左到右+从右到左的和,可能中间有重合,固定一边,求另外一边的和的最大值,保证j >= i+2.
Time complexity: O(n)
Space complexity: O(n)
1 class Solution { 2 public int maxSubarraySumCircular(int[] A) { 3 int curr = 0; 4 int n = A.length; 5 int maxSum = Integer.MIN_VALUE; 6 7 for(int a: A) { 8 curr = a + Math.max(curr, 0); 9 maxSum = Math.max(maxSum, curr); 10 } 11 12 int[] rightMaxSum = new int[n]; 13 rightMaxSum[n-1] = A[n-1]; 14 curr = A[n-1]; 15 for(int i = n-2; i >= 0; --i) { 16 curr += A[i]; 17 rightMaxSum[i] = Math.max(curr, rightMaxSum[i+1]); 18 } 19 20 curr = 0; 21 int result = maxSum; 22 for(int i = 0; i+2 < n; ++i) { 23 curr += A[i]; 24 result = Math.max(result, curr + rightMaxSum[i+2]); 25 } 26 27 return result; 28 } 29 }
Idea 3. Similar to sliding window minValue, build a min deque for prefix sum, for each prefix sum, find the minimum of previous prefixSum so that the subarray sum ending here is the maximu, since it's circular array, j - i <= n, remove the invalid previous index. Note: store the index
Time complexity: O(n)
Space complexity: O(n)
1 class Solution { 2 public int maxSubarraySumCircular(int[] A) { 3 Deque<Integer> minPrefix = new LinkedList<>(); 4 minPrefix.addLast(0); 5 6 int n = A.length; 7 8 int[] prefix = new int[2*n + 1]; 9 for(int i = 0; i < 2*n; ++i) { 10 prefix[i+1] = prefix[i] + A[i%n]; 11 } 12 int result = Integer.MIN_VALUE; 13 for(int i = 1; i <= 2*n; ++i) { 14 15 if(i - minPrefix.getFirst() > n) { 16 minPrefix.removeFirst(); 17 } 18 19 result = Math.max(result, prefix[i] - prefix[minPrefix.getFirst()]); 20 21 while(!minPrefix.isEmpty() && prefix[minPrefix.getLast()] >prefix[i]) { 22 minPrefix.removeLast(); 23 } 24 minPrefix.add(i); 25 26 } 27 28 return result; 29 } 30 }
Note:
-30000 <= A[i] <= 30000
1 <= A.length <= 30000