HARD Lab · Dynamic Programming · 28

53. Maximum Subarray

Find the contiguous subarray with the largest sum using Kadane's algorithm — at each index, decide in one comparison whether to extend the running window or cut the dead weight and restart fresh.

MediumKadaneTypeScript

PROBLEM What we're solving

Given an integer array, return the sum of the contiguous subarray with the largest sum. For nums = [-2,1,-3,4,-1,2,1,-5,4], the answer is 6 — the subarray [4,-1,2,1] at indices 3–6. The array may contain negative numbers; the answer is always at least one element (the problem guarantees a non-empty input).

KEY IDEA Drop the prefix the moment it hurts

Insight → at each position i, the best subarray ending at i is either just nums[i] alone, or curSum + nums[i] (the best ending at i-1, extended). Choose the larger: curSum = max(nums[i], curSum + nums[i]). If curSum was negative, extending with it only hurts — throw it away and start fresh. Track the global maximum as you go.

RECIPE Scan once, extend or restart

  • 0 · Seed. Set curSum = best = nums[0]. This handles the single-element edge case and means we never touch -Infinity.
  • 1 · Scan indices 1 … n-1. For each nums[i]:
    curSum = max(nums[i], curSum + nums[i])
    This one line captures "extend if the running sum helps, restart if it hurts."
  • 2 · Update the global best. best = max(best, curSum) — save the largest running sum seen so far.
  • 3 · Return best. One pass, O(n) time, O(1) space.
Classic confusion → learners often initialize curSum = 0 and then wonder why an all-negative array returns 0 instead of the least-negative element. The fix: seed with nums[0] and start the loop at index 1. Alternatively, the max(nums[i], curSum + nums[i]) form is self-correcting — when curSum < 0 the first branch wins automatically.

COST Complexity & alternatives

Try every subarray
O(n²)
All O(n²) pairs, sum each. Simple but slow.
Kadane's algorithm
O(n)
One pass; O(1) extra space.

Returning the actual subarray

To reconstruct the indices, track a windowStart pointer that resets whenever you restart, and save bestStart / bestEnd whenever you update best. No extra space asymptotically.

Pattern transfer →the same "extend-or-restart running state" idea appears in Maximum Product Subarray (track both max and min because negatives flip sign), Best Time to Buy and Sell Stock(daily profit as the "array", Kadane on differences), and Maximum Sum Circular Subarray (total sum minus minimum subarray).

RUN IT Extend or restart — keep the running max

step 0 / 15
STARTKadane's algorithm — scan left to right, extending or restarting the running sum.
-2
0
1
1
-3
2
4
3
-1
4
2
5
1
6
-5
7
4
8
curSum
0
best
−∞
current indexbest-window barsbest-ending bar (ans)
slowfast

TYPESCRIPT The solution, annotated

maxSubArray.ts
function maxSubArray(nums: number[]): number {
  let curSum = nums[0];
  let best = nums[0];

  for (let i = 1; i < nums.length; i++) {
    // Drop the prefix the moment it hurts: if curSum < 0, extending with it
    // only makes the new subarray smaller than starting fresh at nums[i].
    curSum = Math.max(nums[i], curSum + nums[i]);
    best = Math.max(best, curSum);
  }

  return best;
}

Reading it block by block

Line 2–3 — seed with the first element. Starting both curSum and best at nums[0] correctly handles an all-negative array (the answer is the largest single element, never a fabricated 0).
Line 6–9 — the Kadane update. curSum = max(nums[i], curSum + nums[i]) is the whole algorithm. When curSum is negative, extending it would shrink the total, so the first branch wins and we restart the window at i. When curSum ≥ 0, extending is always at least as good as starting fresh, so the second branch wins.
Line 9 — update global best. After each extension-or-restart, check whether this is the new champion. best only ever grows.
Line 12 — return. One pass, constant extra space. For [-2,1,-3,4,-1,2,1,-5,4] this returns 6.
Complexity → O(n) time — one linear scan with O(1) work per element. O(1) space — only two scalars (curSum, best) beyond the input.

INTERVIEWFollow-ups they'll ask

  • "Return the actual subarray, not just the sum." Add a windowStart that resets to i on restart, and save bestStart / bestEnd whenever best updates.
  • "What if the array is circular?" (LC 918) The answer is either a normal subarray (run Kadane) or wraps around — equivalent to total - minSubarraySum. Take the max of both cases; watch the all-negative edge case.
  • "Maximum product subarray?" (LC 152) Track both maxSoFar and minSoFar because a negative times a negative can become the new max.
  • "What's the brute force?" O(n²) — try every (start, end) pair. Kadane exploits the fact that a subarray with a negative prefix can always be improved by dropping that prefix.
  • "All-negative input?" The answer is the single largest (least negative) element. Seeding with nums[0] handles this; a curSum = 0 seed would silently return 0, which is wrong.

MNEMONIC The one-liner

"A negative running sum is dead weight — drop it the instant it drags you down, and restart fresh."

TRIGGERS When you see ___ → reach for ___

"maximum / minimum contiguous subarray"Kadane's (extend or restart)
running sum turns negative → should we keep it?restart window at cur index
track prefix decision at every stepmax(nums[i], curSum + nums[i])
"best time to buy/sell stock" (daily diffs)Kadane's on profit array

SKELETON The reusable shape

skeleton.ts
function maxSubArray(nums: number[]): number {
  let curSum = nums[0];
  let best   = nums[0];

  for (let i = 1; i < nums.length; i++) {
    curSum = Math.max(nums[i], curSum + nums[i]);
    best   = Math.max(best, curSum);
  }

  return best;
}

FLASHCARDS Tap to flip

What does curSum represent in Kadane's?
The maximum subarray sum ending exactly at the current index.
tap to flip
1 / 7
Answer to reveal explanations. No penalty for retries.Score: 0 / 7
QUESTION 01
What is Kadane's algorithm's time complexity?
QUESTION 02
For nums = [-2,1,-3,4,-1,2,1,-5,4], what is the maximum subarray sum?
QUESTION 03
Why does curSum = Math.max(nums[i], curSum + nums[i]) restart the window when the running sum is negative?
QUESTION 04
What happens if you initialize curSum = 0 (instead of nums[0]) for the input [-3,-1,-2]?
QUESTION 05
What extra information must you track to reconstruct the actual subarray (not just its sum)?
QUESTION 06
Which problem is solved by running Kadane's algorithm on the array of daily price differences?
QUESTION 07
For nums = [1] (single element), what does the algorithm return?