Problem overview
The function maxSumSubarray receives an IntArray named nums that may hold positive and negative numbers. It returns the largest sum that a continuous subarray of nums can have: a run of neighbouring elements, from some start index to some end index, with no gaps.
For nums = [-2, -5, 6, -2, -3, 1, 5, -6] the answer is 7. The subarray [6, -2, -3, 1, 5] adds up to 7, and no other run of neighbours does better. The statement sets a budget of O(n²) time and O(1) extra space, so trying every subarray is acceptable here, as long as it doesn't use an extra array.
Approach: try every start and every end
A subarray is fixed by two indices: the first one, i, and the last one, j, with 0 <= i <= j < n. There are about n²/2 such pairs, so checking them all fits the budget, provided each sum is available in constant time.
Adding up nums[i..j] from scratch for every pair would cost an extra loop and push the total to O(n³). Prefix sums would fix that, but they need an extra array, which breaks the O(1) space limit. The cheaper trick is to fix i and let j grow one step at a time. Moving from j - 1 to j adds exactly one element, nums[j], so one running variable, sum, holds the current subarray's total. When i moves on, the running sum starts again from 0.
On the example, with i = 2 the running sum takes the values 6, 4, 1, 2, 7, 1 as j goes from 2 to 7. The best of those, 7, beats everything seen for the earlier starts, and no later start does better.
Other lessons on the same problem improve on this approach, so the quadratic version here is the baseline. It is the simplest correct solution, and it is the one to write first.
Step by step
- Declare
maxSumasnums[0]. Not 0: every subarray sum can be negative, and the answer must be the sum of a real subarray. The single-element subarraynums[0]is a valid starting candidate. - Loop over the start index
iwithnums.indices, which is the range 0 tonums.size - 1. - Declare
sumas 0 inside that loop. It is declared there because each start needs a fresh, empty running sum, and nothing outside the loop uses it. - Loop over the end index
jfromiup to the last index, usingi until nums.size. Starting atimeans the first subarray tried has a single element. - Add
nums[j]tosum. Nowsumis the total ofnums[i..j]. - Update
maxSumin one line withmaxOf(maxSum, sum), which returns the larger of its two arguments. - Return
maxSumonce both loops are done.
The solution in Kotlin
class Solution {
fun maxSumSubarray(nums: IntArray): Int {
var maxSum = nums[0]
for (i in nums.indices) {
var sum = 0
for (j in i until nums.size) {
sum += nums[j]
maxSum = maxOf(maxSum, sum)
}
}
return maxSum
}
}
The Kotlin details worth knowing:
- Ranges.
i until nums.sizeexcludes its upper bound, sojstops atnums.size - 1.i..nums.size - 1means the same thing. Writingi..nums.sizewould be a bug: the last index it produces is out of bounds. nums.indices. It is the range0..nums.size - 1. It avoids the off-by-one that comes from writing the bound by hand.maxOf. It is a standard-library function that returns the larger of two values, and it works forIntas well as other comparable types.varinside the loop.sumis created again on each pass of the outer loop, so it starts at 0 every time. It's avarbecause+=reassigns it.Intoverflow.Intis a 32-bit type, and arithmetic that goes beyond its range wraps silently without any error. For[2000000000, 2000000000], the two-element sum doesn't fit and wraps to-294967296, so the function returns2000000000. The lesson's inputs stay in range; with larger values, useLongforsumandmaxSum.
Complexity
Time: O(n²). For each start i, the inner loop runs n - i times, so the total number of steps is n + (n - 1) + ... + 1, which is n(n + 1)/2. Each step does constant work: one addition and one comparison.
Space: O(1). The function uses maxSum, sum, i and j, regardless of the size of the input.
Edge cases and common mistakes
- All negative numbers.
[-4, -2, -9]gives-2, the best single element. StartingmaxSumat 0 would wrongly return 0, a sum that no non-empty subarray has. - A single element.
[7]gives7. - All positive numbers.
[1, 2, 3]gives6, the whole array. - Forgetting to reset
sum. Ifsumis declared before the outer loop, each start continues from the previous total, so the result is no longer a subarray sum. - Starting
jat 0 ori + 1. With 0, the code adds elements beforei. Withi + 1, it skips the one-element subarraynums[i], which is wrong for[-10, 5]: the answer is the single 5, but the function would return-5. - An empty array.
nums[0]throws anArrayIndexOutOfBoundsException. The code assumes at least one element.
Testing
[-2, -5, 6, -2, -3, 1, 5, -6]gives7.[-4, -2, -9]gives-2.[7]gives7.[1, 2, 3]gives6.[-10, 5]gives5.