LeetCode 53 asks the solver (you/me) to find the sub-array within an array that has the largest sum. So, for example, if we were given [100,-1, 1] the return would be 100 because the sub-array with the largest sum is [100]. If the array was [100,1,-1], then the return would be 101 because the sub-array with the largest sum is [100,1].
A nice and simple problem.
I know I shouldn’t start with the dumb solution, but I just can’t help myself. I’m finding that it is helping my fingers get used to typing Go, so there is some benefit!
func maxsub-array(nums []int) int {
maxValue := math.MinInt
for start := 0; start < len(nums); start++ {
sum := nums[start]
maxValue = max(maxValue, sum)
for end := start + 1; end < len(nums); end++ {
sum += nums[end]
maxValue = max(maxValue, sum)
}
}
return maxValue
}
Unsurprisingly, this solution is too slow. The runtime complexity is \(O(n^2)\), and nums can have a max length of \(10^5\). So, how can we make it faster?
We can cache the results to avoid duplicate work. Specifically, cache[i] holds the max sub-array sum that ends exactly at index i. That value is either just nums[i] on its own, or nums[i] tacked onto whatever the best sub-array ending at i-1 was, whichever is bigger.
func maxsub-array(nums []int) int {
cache := make([]int, len(nums))
cache[0] = nums[0]
maxValue := cache[0]
for i := 1; i < len(nums); i++ {
cache[i] = max(nums[i], cache[i-1] + nums[i])
maxValue = max(maxValue, cache[i])
}
return maxValue
}
But we can actually make it even faster because we don’t need a cache at all. Look at the recurrence above: cache[i] only ever depends on cache[i-1], never anything further back. So we don’t need the whole array, just the previous value:
func maxsub-array(nums []int) int {
current := nums[0]
maxValue := nums[0]
for i := 1; i < len(nums); i++ {
current = max(nums[i]+current, nums[i])
maxValue = max(maxValue, current)
}
return maxValue
}
To see how this works, let’s see two quick runs with the examples at the top:
Example 1:
Input: [100,-1,1]
current <- 100
maxValue <- 100
i = 1
current <- 99 <- max(-1 + 100, -1)
maxValue <- 100 <- max(100, 99)
i = 2
current <- 100 <- max(1 + 99, 1)
maxValue <- 100 <- max(100, 100)
Output = 100
Example 2:
Input: [100,1,-1]
current <- 100
maxValue <- 100
i = 1
current <- 101 <- max(1 + 100, 1)
maxValue <- 101 <- max(100, 101)
i = 2
current <- 100 <- max(-1 + 101, -1)
maxValue <- 101 <- max(101, 100)
Output = 101
This function works by building the max sub-array until it finds a way to start another larger sum sub-array. And that is the whole solution, beating 100% of submissions in terms of runtime.
Till next time, friends.