Problem overview
The method MaxSumSubarray receives an int[] that may contain negative and positive numbers. It returns the largest sum that any continuous stretch of the array can have. The stretch must contain at least one element.
For [-2, -5, 6, -2, -3, 1, 5, -6] the best stretch is [6, -2, -3, 1, 5], whose sum is 7. The statement asks for O(n) time and O(1) extra space, so the method must make one pass and keep only a few variables.
Approach: the best subarray ending here
Checking every start and end position costs at least O(n²). Kadane's algorithm gets to O(n) with one question for each element: what is the largest sum of a subarray that ends at this element?
That subarray is either the element alone, or the element appended to the best subarray ending at the previous position. Extending only helps when the previous best is positive. A negative running sum just drags the total down, so it is better to start fresh. This gives the recurrence:
maxSum = val + max(maxSum, 0)
The overall answer is the largest maxSum seen at any position, because the best subarray has to end somewhere.
Tracing [-2, -5, 6, -2, -3, 1, 5, -6], the pairs of maxSum and result after each element are: -2 and -2, then -5 and -2, then 6 and 6, then 4 and 6, then 1 and 6, then 2 and 6, then 7 and 7, then 1 and 7. The -5 is not attached to the earlier -2, and the 6 starts a fresh subarray because the previous maxSum was negative. The answer is 7.
Step by step
- Initialise
resultwithnums[0]. It must start at a sum that some subarray really has. Starting at 0 is wrong when every number is negative, because the empty subarray isn't allowed and the answer is then negative. - Initialise
maxSumwith 0. It stands for the best sum ending at the previous element. Before the first element there is nothing, which behaves like 0. - Loop over the elements with a
foreach, reading each one asval. - Update
maxSumwith the recurrence:val + Math.Max(maxSum, 0). Either extend the previous best or start over. - Update
resultwithMath.Max(result, maxSum), sincemaxSumis now the best sum ending at this element. - Return
resultonce the loop is done.
The solution in C#
class Solution {
public int MaxSumSubarray(int[] nums) {
int result = nums[0];
int maxSum = 0;
foreach (int val in nums) {
maxSum = val + Math.Max(maxSum, 0);
result = Math.Max(result, maxSum);
}
return result;
}
}
The C# details to notice:
foreachover an array.foreach (int val in nums)reads each element in order, with no index to manage. The loop variablevalis read-only inside the loop, which is fine because the code never assigns to it.Math.Maxwithintarguments returns anint, so no casts are needed.- Silent overflow.
intarithmetic wraps around without an error by default. For arrays whose sums could exceed about 2.1 billion, makemaxSumandresultlong. For modest values,intis enough. - The input isn't modified. Unlike the sorting lessons, this method only reads
nums.
Complexity
Time: O(n). One pass, with constant work per element.
Space: O(1). Two integer variables, whatever the input size.
Edge cases and common mistakes
- All negative numbers.
[-3, -1, -2]gives -1, the largest single element. This is whyresultstarts atnums[0]and not 0. - A single element.
[4]gives 4. - All positive numbers.
[1, 2, 3]gives 6, the sum of the whole array, becausemaxSumnever resets. - Resetting to 0 instead of to
val. WritingmaxSum = Math.Max(maxSum + val, 0)forces every running sum to be at least 0 and fails on all-negative arrays. The lesson's form keepsvalitself when starting fresh. - Updating
resultbeforemaxSum. The result would then lag one element behind. - Empty array.
nums[0]would throwIndexOutOfRangeException. The problem assumes at least one element.
Testing
[-2, -5, 6, -2, -3, 1, 5, -6]gives 7.[-3, -1, -2]gives -1.[4]gives 4.[1, 2, 3]gives 6.