Problem overview
The function max_sum_subarray receives a vector of integers that may be positive, negative or zero. It returns the largest sum that any non-empty continuous run of elements, a subarray, can have.
For nums = [-2, -5, 6, -2, -3, 1, 5, -6] the answer is 7, from the subarray [6, -2, -3, 1, 5]. The statement requires O(n) time and O(1) extra space, so checking every subarray, which is O(n²) even with running sums, is too slow.
Approach: the best subarray ending at each position
Kadane's algorithm asks a smaller question at every position: what is the largest sum of a subarray that ends exactly here? Call it max_sum. The answer to the whole problem is the biggest max_sum seen at any position, because the best subarray has to end somewhere.
The best subarray ending at the current element val has only two possible shapes. It is either val alone, or it is val attached to the best subarray ending at the previous position. Attaching helps only when that previous sum is positive. This gives the recurrence:
max_sum = val + max(previous max_sum, 0)
A negative running sum only drags the next element down, so it is dropped and the subarray restarts at val. A running sum of exactly 0 changes nothing either way.
Here is the trace on the example:
| val | max_sum | result |
|---|---|---|
| -2 | -2 | -2 |
| -5 | -5 | -2 |
| 6 | 6 | 6 |
| -2 | 4 | 6 |
| -3 | 1 | 6 |
| 1 | 2 | 6 |
| 5 | 7 | 7 |
| -6 | 1 | 7 |
At 6 the running sum restarts, because the sum before it, -5, was negative. After that it grows to 7 and the final -6 can't beat it.
Step by step
- Create
resultfromnums[0]. The answer can be negative, so starting at 0 would wrongly report 0 for an all-negative input. The first element is a real subarray sum, so it is a safe start. - Create
max_sumas 0. Before any element there is an empty prefix, with sum 0. The formula then makes the firstmax_sumequal to the first element. - Loop over
&nums. Borrowing the vector leaves it usable afterwards, and eachvalis a&i32. - Apply the recurrence. Dereference with
*valto get the number, and usemax_sum.max(0)to choose between extending the previous subarray and restarting. - Update
resultwhenmax_sumis bigger. - Return
resultas the last expression, with no semicolon.
The solution in Rust
impl Solution {
fn max_sum_subarray(nums: Vec<i32>) -> i32 {
let mut result: i32 = nums[0];
let mut max_sum: i32 = 0;
for val in &nums {
max_sum = *val + max_sum.max(0);
result = result.max(max_sum);
}
result
}
}
The Rust details worth a closer look:
maxis a method.a.max(b)returns the larger of two integers. It comes from theOrdtrait, somax_sum.max(0)reads as "the larger ofmax_sumand 0". The0literal takes the typei32frommax_sum.- References in the loop. Iterating over
&numsproduces&i32values. The expression*val + ...reads the integer behind the reference. Plainfor val in numswould move the vector into the loop and consume it. - Tail expression. The last line,
result, has no semicolon, so it is the function's return value. Adding a semicolon would make the function return()and the compiler would reject it. - Indexing an empty vector.
nums[0]panics when the vector is empty. The lesson assumes at least one element. - Overflow. The lessons build with optimizations, where
i32arithmetic wraps silently. A debug build would panic instead. Sums beyond about 2.1 billion would needi64.
Complexity
Time: O(n). The loop touches each element once and does a constant amount of work for it.
Space: O(1). Only two integers, result and max_sum, are kept, however long the vector is.
Edge cases and common mistakes
- All negative numbers.
[-3, -1, -2]returns -1, the best single element. Startingresultat 0 would give 0, an empty subarray that the problem doesn't allow. - A single element.
[-4]returns -4. - A negative start.
[-5, 4]returns 4. When 4 arrives, the running sum -5 is negative, somax_sum.max(0)is 0 and the subarray restarts at 4. - Returning
max_sum. The lastmax_sumis the best sum ending at the final element, not the best overall. In the example it is 1, while the answer is 7.
Testing
[-2, -5, 6, -2, -3, 1, 5, -6]gives7.[-3, -1, -2]gives-1.[-4]gives-4.
