Given an integer array nums, return the maximum sum of a non-empty contiguous subarray in the circular array.
In a circular array, the element after the last position is the first element.
Return 0 when nums is empty.
Example 1
Input: nums = [1, -2, 3, -2]
Output: 3
Explanation: The subarray [3] gives the maximum sum 3.
Example 2
Input: nums = [5, -3, 5]
Output: 10
Explanation: The circular subarray [5, 5] gives the maximum sum 10 by wrapping around the end.
Brute Force Approach
A circular subarray can begin at any index and continue forward, wrapping to the beginning when the array ends.
The direct approach starts from every position and extends the subarray one element at a time. Limiting the length to n prevents any element from being selected more than once.
Algorithm
Store the array size in
n. Ifn == 0, return0because no non-empty subarray can be formed.Initialize
maxSumwithnums[0], ensuring that arrays containing only negative values still return a valid non-empty subarray sum.Treat every index
startas the beginning of a circular subarray and initializecurrentSumwith0for that starting position.Extend the subarray from length
1throughn. For each length, calculate the next index as(start + length - 1) % n, where modulo allows the traversal to wrap from the end of the array back to the beginning.Add the selected element to
currentSumand compare it withmaxSumafter every extension because each length represents one valid circular subarray.Return
maxSumafter every starting position and every valid length have been examined.
Dry Run
Maximum Sum Circular Subarray Brute Force Dry Run.png
Solution
#include <bits/stdc++.h>using namespace std;class Solution {public: long long maxCircularSubarraySum(vector<int>& nums) { int n = nums.size(); // No non-empty subarray exists. if (n == 0) { return 0; } long long maxSum = nums[0]; for (int start = 0; start < n; start++) { long long currentSum = 0; /* * Extend from each starting position * while allowing the index to wrap around. */ for (int length = 1; length <= n; length++) { int index = (start + length - 1) % n; currentSum += nums[index]; // Keep the best circular subarray sum found. if (currentSum > maxSum) { maxSum = currentSum; } } } return maxSum; }};int main() { vector<int> nums = {5, -3, 5}; Solution solution; cout << solution.maxCircularSubarraySum(nums) << endl; return 0;}Complexity Analysis
Time Complexity: O(N²), where N represents the array size. Every starting position extends through at most N elements.
Space Complexity: O(1), because only loop indices, currentSum, and maxSum require auxiliary storage.
Better Approach
A wrapping subarray contains two connected parts: a prefix from the beginning and a suffix from the end.
The best suffix available after every prefix boundary can be prepared in advance. Combining each prefix with the best non-overlapping suffix finds the strongest wrapping sum. The normal maximum subarray must also be checked because the answer may not wrap.
Algorithm
Store the array size in
n. If the array is empty, return0.Find
normalMaxusing Kadane’s Algorithm, since the maximum-sum subarray may lie completely inside the normal array without wrapping.Create
maxSuffix, wheremaxSuffix[index]stores the greatest suffix sum obtainable by starting atindexor at any position after it.Build suffix sums from right to left and update
maxSuffixso that each position remembers the strongest suffix available from that point onward.Traverse possible prefix endings from index
0ton - 2. Maintain the current prefix sum and combine it withmaxSuffix[index + 1], ensuring that the selected prefix and suffix remain separate and do not overlap.Return the larger value between
normalMaxand the best prefix-suffix combination, covering both non-wrapping and wrapping subarrays.
Dry Run
Maximum Sum Circular Subarray Better Appraoch Dry Run.png
Solution
#include <bits/stdc++.h>using namespace std;class Solution {public: long long maxCircularSubarraySum(vector<int>& nums) { int n = nums.size(); // No non-empty subarray exists. if (n == 0) { return 0; } long long currentSum = nums[0]; long long normalMax = nums[0]; // Kadane's Algorithm finds the best non-wrapping sum. for (int index = 1; index < n; index++) { currentSum = max( (long long)nums[index], currentSum + nums[index] ); normalMax = max(normalMax, currentSum); } vector<long long> maxSuffix(n); long long suffixSum = nums[n - 1]; maxSuffix[n - 1] = suffixSum; /* * Each position stores the strongest suffix * available from that position onward. */ for (int index = n - 2; index >= 0; index--) { suffixSum += nums[index]; maxSuffix[index] = max( suffixSum, maxSuffix[index + 1] ); } long long prefixSum = 0; long long maxSum = normalMax; /* * Combine each prefix with a suffix that * begins strictly after the prefix ends. */ for (int index = 0; index < n - 1; index++) { prefixSum += nums[index]; long long circularSum = prefixSum + maxSuffix[index + 1]; maxSum = max(maxSum, circularSum); } return maxSum; }};int main() { vector<int> nums = {5, -3, 5}; Solution solution; cout << solution.maxCircularSubarraySum(nums) << endl; return 0;}Complexity Analysis
Time Complexity: O(N), where N represents the array size. Kadane’s traversal, suffix construction, and prefix-suffix combination each require linear time.
Space Complexity: O(N), because maxSuffix stores one value for every array index.
Optimal Approach
The maximum circular subarray can appear in two forms.
The first form is a normal subarray that does not cross the array boundary. Kadane’s Algorithm finds this value directly.
The second form wraps around the boundary. Such a subarray keeps the beginning and end of the array while excluding one middle section. Removing the minimum-sum subarray from the total sum therefore leaves the maximum wrapping sum.
Algorithm
Store the array size in
n. Ifn == 0, return0.Initialize
totalSum,currentMax,maxSum,currentMin, andminSumwithnums[0]. The maximum states track the best normal subarray, while the minimum states track the middle section that may be excluded for a wrapping result.Traverse from index
1, since the first element has already been included in all initial states.Add the current value to
totalSum. UpdatecurrentMaxusing the better choice between starting fresh from the current element and extending the previous maximum-ending subarray. UpdatemaxSumwith the best value seen so far.Similarly, update
currentMinusing the smaller choice between starting fresh and extending the previous minimum-ending subarray, then updateminSum.If
maxSum < 0, returnmaxSumbecause every element is negative and removing the complete minimum subarray would incorrectly create an empty result. Otherwise, calculatetotalSum - minSumas the best wrapping sum and return the larger of the wrapping and normal sums.
Dry Run
Maximum Sum Circular Subarray Optimal Appraoch Dry Run.png
Solution
#include <bits/stdc++.h>using namespace std;class Solution {public: long long maxCircularSubarraySum(vector<int>& nums) { int n = nums.size(); // No non-empty subarray exists. if (n == 0) { return 0; } long long totalSum = nums[0]; long long currentMax = nums[0]; long long maxSum = nums[0]; long long currentMin = nums[0]; long long minSum = nums[0]; for (int index = 1; index < n; index++) { long long currentValue = nums[index]; totalSum += currentValue; // Track the best normal subarray ending here. currentMax = max( currentValue, currentMax + currentValue ); maxSum = max(maxSum, currentMax); /* * Track the minimum subarray whose removal * can produce the best wrapping sum. */ currentMin = min( currentValue, currentMin + currentValue ); minSum = min(minSum, currentMin); } /* * For an all-negative array, removing the * entire minimum subarray would leave nothing. */ if (maxSum < 0) { return maxSum; } long long circularSum = totalSum - minSum; return max(maxSum, circularSum); }};int main() { vector<int> nums = {5, -3, 5}; Solution solution; cout << solution.maxCircularSubarraySum(nums) << endl; return 0;}Complexity Analysis
Time Complexity: O(N), where N represents the array size. Every element is processed once using constant-time updates.
Space Complexity: O(1), because only totalSum, currentMax, maxSum, currentMin, and minSum require auxiliary storage.
FAQs
Q1. Why does totalSum - minSum represent the maximum wrapping sum?
A wrapping subarray keeps elements from the end and beginning of the array. The only excluded elements form one contiguous middle subarray. Removing the smallest possible middle sum leaves the largest wrapping sum.
Q2. Why must the all-negative case be handled separately?
When every value is negative, the minimum subarray is the complete array. Subtracting that sum from totalSum gives 0, which represents selecting no elements and violates the non-empty requirement.
Q3. Why is the normal maximum subarray still required?
The strongest subarray may remain completely inside the array without crossing the circular boundary. The final answer must compare both normal and wrapping possibilities.
Q4. Can a valid circular subarray contain an element more than once?
No. A circular subarray may wrap from the last index to the first, but its length cannot exceed n. Allowing a greater length would reuse array elements.
Q5. How can the actual circular subarray indices be returned?
Track the boundaries of the normal maximum and minimum subarrays. For the wrapping case, the answer contains all indices outside the minimum-sum range.
Q6. Can the Optimal Approach work when exactly one element is present?
Yes. Both the normal maximum and minimum equal that element. The all-negative check or the normal maximum comparison ensures that the single element is returned correctly.
Be the first to add a comment.