Maximum Subarray
ARRAYS & HASHING · MEDIUM
The problem
Section titled “The problem”You’re given an array of integers nums — it may contain negative numbers. Find the contiguous subarray (a run of consecutive elements, no skipping around) that has the largest possible sum, and return that sum — not the subarray itself. The subarray must contain at least one number; you can’t “opt out” and claim an empty subarray sums to zero.
Example: nums = [-2,1,-3,4,-1,2,1,-5,4] → 6, from the subarray [4,-1,2,1]. A single-element array like [1] just returns 1. And because “at least one number” is a hard requirement, an all-negative array like [-3,-1,-8,-2] returns -1 — the least-negative single element — never 0.
Solution 1: Brute force — every window, with a running sum
Section titled “Solution 1: Brute force — every window, with a running sum”The most direct reading of the problem is “try every contiguous window and keep the best sum.” For each start, walk end forward and maintain a running sum, adding just nums[end] each step instead of re-summing the whole window each time. That’s O(n²) total.
function maxSubArrayBrute(nums) { let best = -Infinity; for (let start = 0; start < nums.length; start++) { let sum = 0; for (let end = start; end < nums.length; end++) { sum += nums[end]; best = Math.max(best, sum); } } return best;}Complexity: O(n²) time, O(1) space.
Solution 2: Kadane’s algorithm — extend or restart
Section titled “Solution 2: Kadane’s algorithm — extend or restart”At each index, you only ever need to answer one question: is it better to extend the current run, or restart fresh at this element? Extending is only worth it if the run-so-far is still positive; the moment it drags you down, cut your losses and restart at the current element. Track the best sum seen across every restart.
nums = [-2, 1, -3, 4, -1, 2, 1, -5, 4] — best subarray is [4, -1, 2, 1], sum 6
Press play to run Kadane's single pass.
current sum: — · best sum: —
Step 0 / 9
function maxSubArray(nums) { let cur = nums[0]; let best = nums[0]; for (let i = 1; i < nums.length; i++) { cur = Math.max(nums[i], cur + nums[i]); best = Math.max(best, cur); } return best;}Complexity: O(n) time, O(1) space — one pass, no nested loop.
Complexity comparison
Section titled “Complexity comparison”| Solution | Time | Space |
|---|---|---|
| Brute force (running sum) | O(n²) | O(1) |
| Kadane’s algorithm | O(n) | O(1) |
Same space cost either way — the win here is purely in cutting the nested loop down to a single pass.
Interview corner
Section titled “Interview corner”Q: Why is it safe for currentSum to summarize the entire scan so far, without keeping any other history?
currentSumis defined as “the best sum of a subarray ending exactly at the current index” — that’s a complete answer for this index, not a partial one.- Any subarray ending at index
iis either the single elementnums[i], or some earlier subarray extended bynums[i]— and the best “earlier subarray” for that purpose is exactly the best one ending ati-1, which is what the previouscurrentSumalready holds. - Nothing further back than
i-1can ever matter oncecurrentSumati-1is known — that’s what makes O(1) extra state enough.
Q: Why must maxSum be initialized to nums[0], and not 0?
- The problem requires the subarray to contain at least one number — an empty subarray (implicitly summing to
0) is not a valid answer. - Initializing to
0would silently let that invalid empty subarray win whenever every real subarray sums to something negative — exactly the all-negative-array case, e.g.[-3,-1,-8,-2], whose correct answer is-1, not0. - Seeding both
currentSumandmaxSumwith the first real element instead sidesteps the whole “what does empty even mean here” question.
Q: This solution only returns the maximum sum — how would you adapt it to also return the winning subarray itself?
- Track one more index alongside
currentSum:runStart, the index where the current run began. Reset it toiwhenever the algorithm restarts instead of extends. - Whenever
maxSumupdates, also snapshotbestStart = runStartandbestEnd = i— exactly what the visualizations above are doing under the hood to know which window to highlight as “sorted” at the end. - This adds O(1) extra bookkeeping — no change to the O(n) time or O(1) space bounds.
Q: Trick question — what does this log?
const nums = [];console.log(Math.max(...nums));- Answer:
-Infinity— notundefined, and not a thrown error. Math.max()called with zero arguments (which is exactly whatMath.max(...[])spreads down to) is specified to return-Infinity— the identity value for “the maximum of nothing.”- Both solutions on this page use
Math.maxheavily, but never on a possibly-empty spread — that’s exactly why the “at least one number” guarantee in the problem statement matters in practice. - ⚠ Trap: if you were tempted to “one-line” a solution as something like
Math.max(...allWindowSums), an accidentally-emptynumswouldn’t throw or warn you — it would silently return-Infinity, a wrong-looking number that’s easy to miss in review precisely because nothing crashes.