Skip to content

Kadane and Maximum Subarray

Kadane’s algorithm is the shortest useful algorithm in the interview canon: three lines, O(n)O(n) time, O(1)O(1) space, and it answers “what is the largest sum any contiguous subarray can have?” over arrays that contain negative numbers — where a sliding window cannot go.

The whole thing rests on one decision made once per element:

Does the best subarray ending here extend the previous one, or start fresh at me?

If the running sum you inherit is negative, it can only drag you down, so you restart. That is it. Everything else on this page is that idea wearing a different hat.

What you’ll learn

  • The extend-or-restart decision, and why it is provably optimal.
  • Kadane framed as a one-line DP — which is how you extend it.
  • Why the all-negative case breaks a naive implementation.
  • The three big variants: product (signs flip), circular (wrap around), and absolute value.
  • Three real LeetCode problems solved in the browser: 53, 152, 918.

The cue

The pattern

kadane_template.py
def max_subarray(nums):
    best = current = nums[0]        # NOT 0 -- see the all-negative note
    for x in nums[1:]:
        current = max(x, current + x)   # restart at x, or extend by x
        best = max(best, current)
    return best
 
 
print(max_subarray([-2, 1, -3, 4, -1, 2, 1, -5, 4]))   # 6  -> [4, -1, 2, 1]
print(max_subarray([-3, -1, -2]))                       # -1 -> [-1]
kadane_template.py
def max_subarray(nums):
    best = current = nums[0]        # NOT 0 -- see the all-negative note
    for x in nums[1:]:
        current = max(x, current + x)   # restart at x, or extend by x
        best = max(best, current)
    return best
 
 
print(max_subarray([-2, 1, -3, 4, -1, 2, 1, -5, 4]))   # 6  -> [4, -1, 2, 1]
print(max_subarray([-3, -1, -2]))                       # -1 -> [-1]

currentcurrent means the best sum of any subarray ending exactly at this index. bestbest is the best of those over all indices. Two variables, one pass.

Why extend-or-restart is optimal

Let current[i]current[i] be the largest sum of a subarray ending at index ii. Any such subarray either is just nums[i]nums[i], or is a subarray ending at i-1i-1 extended by nums[i]nums[i]. The best of the second kind is current[i-1] + nums[i]current[i-1] + nums[i]. So

current[i]=max(nums[i], current[i1]+nums[i])\text{current}[i] = \max\big(\text{nums}[i],\ \text{current}[i-1] + \text{nums}[i]\big)

and the answer is maxicurrent[i]\max_i \text{current}[i]. That is a complete DP, and because current[i]current[i] depends only on current[i-1]current[i-1], the table collapses to one variable — which is why the space is O(1)O(1).

Reading it as a DP is what lets you extend it: for the product variant you simply need two rolling states instead of one.

TimeSpace
Every subarray, summedO(n3)O(n^3)O(1)O(1)
Every subarray, running sumO(n2)O(n^2)O(1)O(1)
Divide and conquerO(nlogn)O(n \log n)O(logn)O(\log n)
KadaneO(n)O(n)O(1)O(1)

The variant map

VariantWhat changesCanonical problem
Maximum sumThe base template53 Maximum Subarray
Maximum productTrack max and min; a negative swaps them152 Maximum Product Subarray
CircularAnswer is max(best_max, total - best_min)max(best_max, total - best_min), guarded918 Maximum Sum Circular Subarray
Maximum absolute summax(best_max, -best_min)max(best_max, -best_min)1749
Return the indicesRecord startstart when you restart, and (start, i)(start, i) when bestbest improves53 follow-up
Minimum sumFlip every maxmax to minminUsed as a subroutine by 918

Practice — real LeetCode problems

LC 53 — Maximum Subarray · Medium

Problem. Given an integer array numsnums, find the contiguous subarray with at least one element that has the largest sum, and return that sum.

Constraints. 1 <= len(nums) <= 10^51 <= len(nums) <= 10^5, -10^4 <= nums[i] <= 10^4-10^4 <= nums[i] <= 10^4.

Examples. [-2,1,-3,4,-1,2,1,-5,4][-2,1,-3,4,-1,2,1,-5,4] gives 66 ([4,-1,2,1][4,-1,2,1]) · [1][1] gives 11 · [5,4,-1,7,8][5,4,-1,7,8] gives 2323 · [-3,-1,-2][-3,-1,-2] gives -1-1

Editorial — approach, complexity, follow-ups

currentcurrent is the best sum of a subarray ending at the current index. If the inherited currentcurrent is negative, xx alone beats current + xcurrent + x, so the subarray restarts — which is what max(x, current + x)max(x, current + x) expresses without an explicit branch.

Time O(n)O(n). Space O(1)O(1).

The [-3,-1,-2][-3,-1,-2] case is the whole reason to be careful with initialisation. Every currentcurrent is negative, every restart is negative, and the correct answer is the least bad single element.

Follow-ups you should expect:

  • “Return the subarray, not the sum.” Keep a startstart that resets to ii whenever you restart, and record (start, i)(start, i) whenever bestbest improves.
  • “Do it with divide and conquer.” LeetCode explicitly asks for this. Split in half; the answer is the best of (left half, right half, best crossing subarray), where the crossing one is found by expanding outward from the midpoint. O(nlogn)O(n \log n) — strictly worse, but it is a legitimate demonstration of the technique and a common ask.
  • “What if the array is a stream?” Kadane is already online: it needs only the current element and two numbers of state.
  • “At most kk elements?” No longer Kadane — that is a windowed DP needing a monotonic deque.

LC 152 — Maximum Product Subarray · Medium

Problem. Given an integer array numsnums, find the contiguous subarray with the largest product, and return that product.

Constraints. 1 <= len(nums) <= 2 * 10^41 <= len(nums) <= 2 * 10^4, -10 <= nums[i] <= 10-10 <= nums[i] <= 10. The answer fits in a 32-bit integer.

Examples. [2,3,-2,4][2,3,-2,4] gives 66 ([2,3][2,3]) · [-2,0,-1][-2,0,-1] gives 00 · [-2,3,-4][-2,3,-4] gives 2424 (the whole array) · [0,2][0,2] gives 22

Editorial — approach, complexity, follow-ups

Sums are monotonic under extension; products are not. Multiplying by a negative number inverts the ordering, so the smallest product so far is a genuine candidate for the largest product next. Carrying only hihi throws away exactly the information you need on [-2,3,-4][-2,3,-4], where the answer uses both negatives.

So maintain:

  • hihi = largest product of a subarray ending here,
  • lolo = smallest product of a subarray ending here.

When x < 0x < 0, swap them first. Then update both against xx alone (which is the “restart here” option) and against the extended product.

Time O(n)O(n). Space O(1)O(1).

Follow-ups you should expect: “Why not divide by the outgoing element to slide a window?” — because a zero makes division undefined and destroys the invariant. “Maximum absolute sum (1749)?” — run Kadane for max and min, and return max(best_max, -best_min)max(best_max, -best_min). “Count of negatives approach?” — there is a neat prefix/suffix-scan solution too, worth mentioning: scan left-to-right and right-to-left, resetting on zeros, and take the best product seen.

LC 918 — Maximum Sum Circular Subarray · Medium

Problem. Given a circular integer array numsnums, return the maximum sum of a non-empty subarray. The subarray may wrap from the end back to the beginning, but may not include any element more than once.

Constraints. 1 <= len(nums) <= 3 * 10^41 <= len(nums) <= 3 * 10^4, -3 * 10^4 <= nums[i] <= 3 * 10^4-3 * 10^4 <= nums[i] <= 3 * 10^4.

Examples. [1,-2,3,-2][1,-2,3,-2] gives 33 · [5,-3,5][5,-3,5] gives 1010 (wraps) · [-3,-2,-3][-3,-2,-3] gives -2-2 · [-2,4,-5,4,-5,9,4][-2,4,-5,4,-5,9,4] gives 1515

Editorial — approach, complexity, follow-ups

Split into two cases:

  1. The answer does not wrap. Then it is ordinary Kadane: best_maxbest_max.
  2. The answer wraps. Then the elements it excludes form a single contiguous, non-wrapping block in the middle. Maximising the wrapped sum is therefore the same as minimising the excluded block: total - best_mintotal - best_min.

Run Kadane twice in the same pass — once with maxmax, once with minmin — and return the better of the two candidates.

Time O(n)O(n), single pass. Space O(1)O(1).

Follow-ups you should expect: “Why can’t you just concatenate the array with itself and run Kadane?” — because that permits subarrays longer than nn, reusing elements; you would need a length cap, which turns it into a windowed DP with a monotonic deque. “Return the indices?” — track them in both Kadanes and report from whichever branch won. “Circular minimum subarray?” — mirror the whole argument.

LeetCode problem set

#ProblemDifficultyThe twist
121Best Time to Buy and Sell StockEasyKadane over day-to-day differences — or just track the running minimum
53Maximum SubarrayMediumThe base template; the all-negative case is the trap
152Maximum Product SubarrayMediumTwo rolling states; a negative swaps max and min
918Maximum Sum Circular SubarrayMediumtotal - min_subarraytotal - min_subarray for the wrapping case, plus the all-negative guard
1749Maximum Absolute Sum of Any SubarrayMediumRun both Kadanes, return max(best_max, -best_min)max(best_max, -best_min)
2606Find the Substring With Maximum CostMediumMap characters to values, then plain Kadane — here an empty answer is allowed, so 00 is a valid floor

Interview follow-ups

They askWhat they’re checkingThe answer
“Why does extend-or-restart work?”Whether you can justify itIt is the DP current[i] = max(nums[i], current[i-1] + nums[i])current[i] = max(nums[i], current[i-1] + nums[i]); a negative inherited sum can only reduce any extension, so discarding it is never worse
“Can the subarray be empty?”Whether you clarify the specAsk. If yes, floor at 00; if no, start from nums[0]nums[0]. It changes the answer on all-negative input
“Return the subarray itself”BookkeepingReset start = istart = i on a restart; save (start, i)(start, i) when bestbest improves
“Divide and conquer version”Breadth (LC 53 asks explicitly)Best of left, right, and the best crossing subarray from the midpoint — O(nlogn)O(n \log n)
“Why two states for the product?”DepthMultiplying by a negative inverts the ordering, so the running minimum is a candidate for the next maximum
“Handle a stream”PracticalityAlready online — O(1)O(1) state, one element at a time
“At most kk elements”Knowing the boundaryNo longer Kadane; it becomes a windowed DP + monotonic deque

Edge-case checklist

  • All negative — the headline trap. Answer is the largest single element, not 00.
  • Single element — must return that element, even if negative.
  • All positive — the answer is the entire array; a sanity check.
  • Contains zeros — harmless for sums; the reset case for products.
  • Empty subarray allowed? — a real specification question (2606 vs 53); ask rather than assume.
  • Wrapping answer covering the whole array (918) — possible when all values are positive; make sure your formula does not exclude everything.
  • Two negatives making a positive product (152) — [-2,3,-4][-2,3,-4] gives 2424; the case that catches single-state solutions.

Recap

  • Kadane is one decision per element: extend the previous subarray, or restart herecurrent = max(x, current + x)current = max(x, current + x).
  • It is a collapsed DP, which is why it is O(n)O(n) time and O(1)O(1) space, and why the variants are just “carry more state”.
  • Never initialise to 00 unless the empty subarray is explicitly allowed — otherwise all-negative input returns the wrong answer.
  • Product needs two states because negatives invert the ordering.
  • Circular reframes a wrapping subarray as the contiguous block it excludes: max(best_max, total - best_min)max(best_max, total - best_min), guarded against all-negative input.
  • Kadane finds the best subarray; counting subarrays is prefix sums + a hash map instead.

Next: Cyclic Sort — the trick for arrays that are a permutation of 1..n1..n, where the values themselves tell you where they belong.

If this helped you, consider buying me a coffee ☕

Buy me a coffee

Was this page helpful?

Let us know how we did