Maximum Subarray
Maximum Subarray
Given an array of integers, find the contiguous subarray with the largest sum. Brute-force over all subarrays is O(n^2).
Why this is worth learning
The useful state is the best nonempty subarray ending exactly here, not the best subarray anywhere. A negative preceding sum can only hurt a segment that includes the current value.
Before coding: one-minute experiment
For [-5,-2,-8], what is the answer and why is initializing the answer to zero incorrect?
Function signature
func MaxSubArray(nums []int) int
Inputs / outputs
nums: non-empty array of integers.- Return: maximum subarray sum.
Example
nums = [-2,1,-3,4,-1,2,1,-5,4]
Best subarray: [4,-1,2,1] -> 6
Output: 6
Constraints
- 1 <= len(nums) <= 100_000
- Values are Go
int; the mathematical maximum subarray sum is guaranteed to fitinton the target. - The input is read-only. Negative sums of discarded subarrays need not fit
int.
Core idea (near-solution)
Kadane's algorithm: track the best subarray ending at each position.
Invariant: cur is the best sum of a subarray that must end at i.
Algorithm (step-by-step)
- Initialize
cur = best = nums[0]. - For each i from 1..:
- If
cur < 0, resetcur = nums[i]before doing any addition. - Otherwise extend:
cur += nums[i]. best = max(best, cur).
- If
- Return
best.
Correctness sketch
Every nonempty subarray ending at index i is either the singleton nums[i] or an extension of a subarray ending at i-1. Among extensions, the previous maximum ending sum gives the largest total because the same nums[i] is added to every candidate. Thus E(i)=max(nums[i],E(i-1)+nums[i]). A separate best value takes the maximum of E over all processed endpoints, covering every possible nonempty subarray. Initializing both states from nums[0], rather than zero, preserves the required nonempty choice when all values are negative.
Implement the mathematical maximum by branching before addition. If cur < 0, extending is always worse than taking the new value alone, so that losing sum need not be evaluated. Otherwise cur + nums[i] cannot underflow, and it cannot exceed the representable global maximum promised by the input contract. This preserves the recurrence without requiring every discarded subarray sum to fit.
Trace (hand walkthrough)
Example input:
nums = [-2,1,-3,4,-1,2,1,-5,4]
Best subarray: [4,-1,2,1] -> 6
Output: 6
Walk:
- cur=-2 best=-2
- val=1 -> cur=1 best=1; val=-3 -> cur=-2
- val=4 -> cur=4 best=4; val=-1 -> cur=3
- val=2 -> cur=5; val=1 -> cur=6 best=6
Pitfalls to avoid
- Initializing
curto 0 (fails for all-negative arrays). - Confusing subarray (contiguous) with subsequence.
- Eagerly computing
max(x, cur+x)in fixed-width arithmetic. On a 64-bit target,[-9223372036854775808,-1]has answer-1, but the discarded addition wraps to9223372036854775807.
Debugging lens
Test all-negative inputs, one value, zeros, and a reset after the smallest representable integer. The recurrence is about subarrays, not arbitrary subsequences. Reset before computing a losing negative extension.
Complexity
- Time:
O(n). - Extra space:
O(1).
Implementation notes (Go)
- Use
intand branch before adding. The maximum answer must fit the targetint; the function does not report overflow for inputs outside that guarantee.
Reconstruction checkpoint
For [-5,-2,-8], what is the answer and why is initializing the answer to zero incorrect?
Model answer
The answer is -2, the singleton middle element. Zero represents an empty subarray, which is not an allowed candidate.