Algorithm

Maximum Subarray (Kadane's)

Arrays & Strings Pattern

Maximum Subarray (Kadane's)

Given an integer array nums, find the contiguous subarray (containing at least one number) which has the largest sum and return its sum.

CONSTRAINTS
  • 1 <= nums.length <= 10⁵
  • -10⁴ <= nums[i] <= 10⁴
EXAMPLE 1
Input: nums = [-2,1,-3,4,-1,2,1,-5,4]
Output: 6
The best slice is [4, -1, 2, 1] with sum 6. Note it keeps the -1 inside: its neighbors more than repay the loss, and any slice avoiding it does worse.
EXAMPLE 2
Input: nums = [1]
Output: 1
Only one non-empty subarray exists — the single element itself.
EXAMPLE 3
Input: nums = [5,4,-1,7,8]
Output: 23
The whole array wins: even the -1 is worth keeping for the 7 and 8 behind it.
EXAMPLE 4
Input: nums = [-3,-5,-1]
Output: -1
All negative. An empty subarray is not allowed, so the answer is the least negative single element, -1 — not 0.
Does 'subarray' mean the elements must be contiguous?
Yes — an unbroken slice of the array, in its original order. Picking non-adjacent elements would be a subsequence, which is a different problem. Always confirm this word with the interviewer.
If all numbers are negative, do I return 0 for an empty subarray?
No. The subarray must contain at least one number, so with all negatives the answer is the single least-negative element.
Do I need to return the actual subarray, or just the sum?
Just the sum. (The scan can be extended to also record start and end indices if a follow-up asks for the slice itself.)

First, be precise about the word subarray, because interviews are lost on it. A subarray is a contiguous slice of the array: you choose a start position and an end position and take everything between them — no gaps, no reordering. In [5, 4, -1, 7], the slice [4, -1] is a subarray, but [5, 7] is not, because 5 and 7 are not neighbors. (A selection that may skip elements is called a subsequence — a different concept that belongs to different problems.) Our job: among all subarrays containing at least one number, find the one with the largest sum, and return that sum. The "at least one" clause matters: if every number is negative, the right answer is the least negative single element — we are never allowed to answer 0 by taking nothing.

Counting the windows: the honest brute force

A subarray is pinned down by two choices — where it starts and where it ends. A start at position i can pair with any of the ends at or after it, so an array of N elements has N + (N-1) + ... + 1, roughly N²/2, subarrays. Checking them all is the brute force. One economy is available immediately: holding the start fixed and sliding the end one step right extends the sum by a single addition — no need to re-add the whole window. That brings the cost down to one addition per window: O(N²).

python
max_sum = nums[0]
for i in range(len(nums)):            # choose where the subarray starts
    current_sum = 0
    for j in range(i, len(nums)):     # slide the end one step at a time
        current_sum += nums[j]        # extend the running sum by one number
        max_sum = max(max_sum, current_sum)
return max_sum

(max(a, b) simply hands back the larger of its two inputs.) At this problem's limit, N = 100,000, that is around five billion additions — far too slow. And the waste has a shape: the same stretches of numbers get re-summed inside many overlapping windows. We want to sweep the array once and never re-add anything.

The reframe: the best subarray ending at each position

Here is the thought that cracks the problem open. Every subarray ends somewhere. So imagine we knew, for each position i, the answer to a much smaller question: "among the subarrays that end exactly at position i, which has the largest sum?" The overall winner must be the best of those N answers — every subarray ends at one of the N positions, so none can escape.

And the smaller question turns out to be nearly free to answer. A subarray ending at position i has only two possible shapes: it is the single element nums[i] by itself, or it is some subarray ending at position i - 1 with nums[i] glued on the end. If we are gluing, we should obviously glue onto the best subarray ending at i - 1 — a larger previous sum gives a larger total. Nothing else needs to be considered.

Key Insightbest ending at i = max(nums[i] alone, best ending at i-1 + nums[i]). In plain words — at every element there are exactly two options: extend the current streak, or start fresh here. Notice when "start fresh" wins: precisely when the previous streak's sum is negative. A negative running streak is debt — gluing it onto nums[i] can only drag the total down.
Kadane's algorithm: the reframe as code

One value flows into the next, so the whole computation is a single pass with two variables.

python
current_streak = 0                                # best sum ending at the previous position
max_result = nums[0]                              # best answer seen anywhere so far
for x in nums:
    current_streak = max(x, current_streak + x)   # start fresh at x, or extend the streak?
    max_result = max(max_result, current_streak)  # record the best "ending here"
return max_result

Be clear about what each variable means. current_streak is the answer to "best subarray ending right here"; max_result remembers the best of those answers across the whole walk. And note that max_result starts at nums[0], not at 0 — that is what makes the all-negative case come out right. The algorithm is never allowed to pretend an empty subarray with sum 0 exists.

Worked Example:nums = [-2, 1, -3, 4, -1, 2, 1, -5, 4]
- x = -2: streak = max(-2, 0 + (-2)) = -2. Best so far: -2.
- x = 1: streak = max(1, -2 + 1) = 1 — start fresh, dumping the -2 debt. Best: 1.
- x = -3: streak = max(-3, 1 - 3) = -2 — extending still beats -3 alone. Best stays 1.
- x = 4: streak = max(4, -2 + 4) = 4 — start fresh; the old streak was pure debt. Best: 4.
- x = -1: streak = max(-1, 4 - 1) = 3 — extend; the streak absorbs the -1. Best stays 4.
- x = 2: streak = max(2, 3 + 2) = 5. Best: 5.
- x = 1: streak = max(1, 5 + 1) = 6. Best: 6.
- x = -5: streak = max(-5, 6 - 5) = 1 — keep extending; 1 in hand beats restarting at -5. Best stays 6.
- x = 4: streak = max(4, 1 + 4) = 5. Best stays 6.
- Answer: 6, the sum of the slice [4, -1, 2, 1].
Worked Example:nums = [-3, -5, -1] — all negative
- x = -3: streak = -3. Best: -3.
- x = -5: streak = max(-5, -3 - 5) = -5 — "start fresh" even into a negative: -5 beats -8. Best stays -3.
- x = -1: streak = max(-1, -5 - 1) = -1. Best: -1.
- Answer: -1. Forced to take at least one element, we take the least damaging one.
What you bought, and what to keep

One pass, one addition and two comparisons per element: O(N) time, O(1) extra space — from five billion operations down to a hundred thousand. But the reframe is worth far more than this single problem. Whenever a question asks for the best contiguous stretch of anything, ask: "what is the best stretch ending exactly here, and how does it follow from the one ending one step earlier?" Solving one small question per position, each built from the previous answer, is your first taste of a technique called dynamic programming — and you will meet it again immediately: the very next problem swaps addition for multiplication, and watching exactly where today's reasoning breaks is the whole point of it.

Interactive Strategy Visualization

The Streak Decision

Strategy: Kadane's Choice
Max Found: -2
-2
1
-3
4
-1
2
1
-5
4
Current Bag
-2
Starting with -2. Since it's negative, it immediately drags our streak down.
O(N²) Brute Force
O(N) Kadane's