DSA · Arrays & DP

Kadane's Algorithm: Maximum Subarray Sum and Every Variant Interviewers Ask

Why the one-line recurrence works, a proof you can say in an interview, and the circular, product, one-deletion, absolute-sum and 2D rectangle variants with tested C++, Java and Python.

✍️ Pranjal Jain, Ex-Microsoft · IIT Kanpur 📅 10 October 2026 ⏱ 15 min read
A mountain ridge with one continuous glowing segment, representing the maximum sum contiguous subarray
TL;DR
  • Recurrence: cur = max(x, cur + x); best = max(best, cur). Initialise both with nums[0] so all-negative arrays work. O(n) time, O(1) space.
  • Meaning: cur is the best sum of a subarray ending here. A negative running sum can only hurt what follows, so drop it.
  • Circular: max(kadaneMax, total - kadaneMin), except return kadaneMax when it is negative.
  • Max product: track both max and min ending here; swap them on a negative number.
  • 2D rectangle: fix top and bottom rows, compress columns into sums, run Kadane. O(R² · C).

Maximum Subarray (LeetCode 53) is one of the most frequently asked array problems in Indian interviews, from campus OAs to FAANG phone screens. The base solution is three lines, which is exactly why interviewers rarely stop there: they follow up with "return the indices", "what if it is circular?", "what about products?", or "now do it on a matrix". This guide gives you the base algorithm with a proof you can say out loud, then every common variant, each with code that has been checked against brute force on thousands of random arrays.

The Maximum Subarray Problem

Given an integer array, find the contiguous, non-empty subarray with the largest sum. For [-2, 1, -3, 4, -1, 2, 1, -5, 4] the answer is 6, from [4, -1, 2, 1].

ApproachTimeSpaceIdea
Brute force, triple loopO(n³)O(1)Every (i, j), sum the range
Brute force with running sumO(n²)O(1)Fix i, extend j while accumulating
Prefix sums, min prefix so farO(n)O(1)sum(i..j) = P[j] - P[i-1]; maximise by keeping the minimum earlier prefix
Divide and conquerO(n log n)O(log n)Best in left half, right half, or crossing the middle
KadaneO(n)O(1)Best subarray ending at each index

The prefix-sum view is worth noticing: maximising P[j] - P[i-1] is the same problem as Best Time to Buy and Sell Stock (buy at the minimum prefix, sell at the current one). Kadane and "min so far" are two faces of one idea; the prefix sum guide explores the other face.

Kadane's Intuition: Never Carry a Negative Prefix

Walk left to right and ask, at every index i: what is the best subarray that ends exactly here? There are only two candidates:

  • The element alone: nums[i].
  • The best subarray ending at i - 1, extended by nums[i].

The second is better exactly when the previous best is positive. So whenever the running sum goes negative, it is dead weight: any subarray that included it would be better off starting later. That is the whole algorithm. The global answer is the best of these "ending here" values over all i, because the optimal subarray has to end somewhere.

Kadane's Algorithm Code and Dry Run

C++
long long maxSubArray(const vector<int>& nums) {
    long long best = nums[0], cur = nums[0];
    for (size_t i = 1; i < nums.size(); i++) {
        cur = max<long long>(nums[i], cur + nums[i]);   // extend or restart
        best = max(best, cur);
    }
    return best;
}
Java
public static int maxSubArray(int[] nums) {
    int best = nums[0], cur = nums[0];
    for (int i = 1; i < nums.length; i++) {
        cur = Math.max(nums[i], cur + nums[i]);
        best = Math.max(best, cur);
    }
    return best;
}
Python
def max_subarray(nums):
    best = cur = nums[0]
    for x in nums[1:]:
        cur = max(x, cur + x)
        best = max(best, cur)
    return best

Dry run on [-2, 1, -3, 4, -1, 2, 1, -5, 4]

ixcur + xcur = max(x, cur + x)bestDecision
0-2--2-2start
11-111restart (prefix was negative)
2-3-2-21extend
34244restart
4-1334extend
52555extend
61666extend
7-5116extend
84556extend

Answer 6. For an all-negative array like [-3, -1, -2] the same code returns -1, the largest single element, because max(x, cur + x) always prefers a lone negative over adding another negative.

⚠️
The "reset to zero" versionA popular textbook form is cur += x; best = max(best, cur); if (cur < 0) cur = 0; with best = 0. It returns 0 for all-negative input, which is the answer only if an empty subarray is allowed. LeetCode 53 requires non-empty, so either start best at nums[0] (or negative infinity) or use the max form above. Ask the interviewer whether an empty subarray counts.

Why Kadane's Algorithm Is Correct

Define E[i] = maximum sum of a non-empty subarray that ends at index i. Claim: E[i] = max(nums[i], E[i-1] + nums[i]).

Proof. Any subarray ending at i is either [i..i], with sum nums[i], or [j..i] for some j < i, whose sum is sum(j..i-1) + nums[i]. The largest possible sum(j..i-1) over all j is by definition E[i-1]. So the best of the second kind is E[i-1] + nums[i], and E[i] is the max of the two kinds. The base case E[0] = nums[0] is trivial, so by induction every E[i] is computed correctly.

The optimal subarray overall ends at some index k, so its sum is E[k], and max over i of E[i] is the answer. Since E[i] only depends on E[i-1], one variable suffices: that is the space optimisation from O(n) to O(1).

Saying "dp[i] is the best subarray ending at i; it either extends dp[i-1] or starts fresh" in an interview is the proof they want. It also explains why this counts as dynamic programming rather than a lucky greedy trick, which is a common follow-up question.

Returning the Subarray, Not Just the Sum

Track where the current run started. When you restart at i, the candidate start becomes i; when the current sum beats the best, record the candidate start and i as the best range.

Python
def max_subarray_with_indices(nums):
    best, best_l, best_r = nums[0], 0, 0
    cur, cur_l = nums[0], 0
    for i in range(1, len(nums)):
        if cur < 0:                 # restarting is strictly better
            cur, cur_l = nums[i], i
        else:
            cur += nums[i]
        if cur > best:
            best, best_l, best_r = cur, cur_l, i
    return best, best_l, best_r     # (6, 3, 6) for the example

Ties: this version keeps the earliest best subarray and, because it extends when cur == 0, prefers longer runs over restarting. If the interviewer wants the shortest or the latest among equal sums, adjust the < and > to <= and >= accordingly and say which rule you chose.

Maximum Sum Circular Subarray (LeetCode 918)

Now the array wraps: a subarray may run off the end and continue from the start. The optimal subarray is one of two shapes:

  1. No wrap. A normal subarray: plain Kadane gives it.
  2. Wraps around. It covers a suffix and a prefix, so what it leaves out is a contiguous middle block. Maximising what you keep is the same as minimising what you leave out: answer = total - (minimum subarray sum).
C++
int maxSubarraySumCircular(const vector<int>& nums) {
    int total = 0, curMax = 0, curMin = 0;
    int bestMax = nums[0], bestMin = nums[0];
    for (int x : nums) {
        curMax = max(x, curMax + x);
        bestMax = max(bestMax, curMax);
        curMin = min(x, curMin + x);
        bestMin = min(bestMin, curMin);
        total += x;
    }
    // all negative: the "wrap" would be the empty subarray, which is not allowed
    return bestMax < 0 ? bestMax : max(bestMax, total - bestMin);
}
Python
def max_subarray_circular(nums):
    total = 0
    cur_max = cur_min = 0
    best_max = best_min = nums[0]
    for x in nums:
        cur_max = max(x, cur_max + x)
        best_max = max(best_max, cur_max)
        cur_min = min(x, cur_min + x)
        best_min = min(best_min, cur_min)
        total += x
    if best_max < 0:
        return best_max
    return max(best_max, total - best_min)

Examples: [5, -3, 5]: no-wrap best is 7 (whole array), min subarray is -3, total 7, wrap gives 10 from [5, 5] across the boundary. Answer 10. [-3, -2, -3]: bestMax = -2 is negative, so return -2. Without that guard, total - bestMin = -8 - (-8) = 0, an empty subarray, which is the classic bug.

An alternative solution uses prefix sums with a monotonic deque over a doubled array (window length at most n), which generalises to "circular with a maximum length" but costs O(n) extra space. The two-Kadane trick is what interviewers expect.

Maximum Product Subarray (LeetCode 152)

Products break plain Kadane because a negative number flips order: the most negative product so far becomes the largest after multiplying by a negative. So track both the maximum and minimum product ending at each index.

C++
int maxProduct(const vector<int>& nums) {
    int best = nums[0], hi = nums[0], lo = nums[0];
    for (size_t i = 1; i < nums.size(); i++) {
        int x = nums[i];
        if (x < 0) swap(hi, lo);   // a negative turns the min into the max
        hi = max(x, hi * x);
        lo = min(x, lo * x);
        best = max(best, hi);
    }
    return best;
}
Python
def max_product(nums):
    best = hi = lo = nums[0]
    for x in nums[1:]:
        if x < 0:
            hi, lo = lo, hi
        hi = max(x, hi * x)
        lo = min(x, lo * x)
        best = max(best, hi)
    return best

Dry run on [-2, 3, -4]: start hi = lo = -2. At 3: hi = max(3, -6) = 3, lo = min(3, -6) = -6. At -4: swap gives hi = -6, lo = 3; then hi = max(-4, 24) = 24, lo = min(-4, -12) = -12. Answer 24, the whole array. Zeros reset both trackers automatically, since max(0, ...) and min(0, ...) restart at 0. There is also a neat two-pass alternative (maximum of running prefix and suffix products, resetting at zero) covered in the dedicated maximum product subarray post.

Variants: One Deletion, Absolute Sum and Stock Profit

Maximum Subarray Sum with One Deletion (LeetCode 1186)

You may delete at most one element (the result must stay non-empty). Keep two states ending at i: keep = best sum with no deletion (plain Kadane), and deleted = best sum with exactly one deletion used. Either the deletion happened earlier and we extend (deleted + x), or we delete x itself and take the previous keep.

Python
def maximum_sum_one_deletion(arr):
    keep = arr[0]
    deleted = float('-inf')        # cannot delete the only element at i = 0
    best = arr[0]
    for x in arr[1:]:
        deleted = max(deleted + x, keep)   # uses OLD keep: delete x
        keep = max(keep + x, x)
        best = max(best, keep, deleted)
    return best

[1, -2, 0, 3] gives 4 (delete -2), [1, -2, -2, 3] gives 3, and [-1, -1, -1, -1] gives -1. This "Kadane with a small state machine" generalises: at most k deletions means k + 1 states.

Maximum Absolute Sum of Any Subarray (LeetCode 1749)

Run max-Kadane and min-Kadane together and return max(best, -worst). Allowing empty subarrays (reset to 0) is fine here since the answer is at least 0.

Python
def max_absolute_sum(nums):
    hi = lo = best = 0
    for x in nums:
        hi = max(0, hi + x)
        lo = min(0, lo + x)
        best = max(best, hi, -lo)
    return best        # 5 for [1,-3,2,3,-4], 8 for [2,-5,1,-4,3,-2]

Best Time to Buy and Sell Stock (LeetCode 121) as Kadane

Profit from buying on day i and selling on day j is the sum of daily changes prices[k] - prices[k-1] for k in (i, j]. So the maximum profit is the maximum subarray of the difference array, floored at 0. For [7, 1, 5, 3, 6, 4] the differences are [-6, 4, -2, 3, -2], whose best subarray is 4 - 2 + 3 = 5. Seeing this equivalence is a nice moment in an interview.

2D Kadane: Maximum Sum Rectangle in a Matrix

Find the sub-rectangle with the largest sum. Brute force over all rectangles with 2D prefix sums is O(R² · C²). The trick: fix a top row and a bottom row. Collapse every column between them into one number (its sum over those rows); now the best rectangle with those vertical bounds is just the maximum subarray of that 1-D array.

Python
def max_sum_rectangle(matrix):
    rows, cols = len(matrix), len(matrix[0])
    best = float('-inf')
    for top in range(rows):
        col_sum = [0] * cols
        for bottom in range(top, rows):
            for c in range(cols):
                col_sum[c] += matrix[bottom][c]     # extend band by one row
            best = max(best, max_subarray(col_sum))
    return best
band = rows 1..3 (0-indexed) of the matrix below row 1: [ -8 -3 4 2 1 ] row 2: [ 3 8 10 1 3 ] row 3: [ -4 -1 1 7 -6 ] ───────────────────── sums: [ -9 4 15 10 -2 ] ──► Kadane ──► 4 + 15 + 10 = 29

Complexity: O(R²) bands, each updated in O(C) and scanned by Kadane in O(C), total O(R² · C). If R > C, transpose first so the squared term is the smaller dimension. On the classic textbook matrix [[1,2,-1,-4,-20],[-8,-3,4,2,1],[3,8,10,1,3],[-4,-1,1,7,-6]] the answer is 29 (rows 1 to 3, columns 1 to 3).

The harder follow-up, Max Sum of Rectangle No Larger Than K (LeetCode 363), keeps the same band idea but replaces Kadane with "best subarray sum ≤ k", solved with prefix sums and a sorted set (lower_bound(prefix - k)), for O(R² · C log C). More grid techniques are in the matrix and grid problems guide.

The Divide and Conquer Follow-Up

LeetCode 53's statement literally suggests trying divide and conquer, and some interviewers ask for it to test recursion. The best subarray lies entirely in the left half, entirely in the right half, or crosses the midpoint. The crossing case is the best suffix of the left half plus the best prefix of the right half, computed in linear time.

Python
def max_subarray_dc(nums):
    def solve(lo, hi):
        if lo == hi:
            return nums[lo]
        mid = (lo + hi) // 2
        best_left_suffix, s = float('-inf'), 0
        for i in range(mid, lo - 1, -1):
            s += nums[i]
            best_left_suffix = max(best_left_suffix, s)
        best_right_prefix, s = float('-inf'), 0
        for i in range(mid + 1, hi + 1):
            s += nums[i]
            best_right_prefix = max(best_right_prefix, s)
        cross = best_left_suffix + best_right_prefix
        return max(solve(lo, mid), solve(mid + 1, hi), cross)
    return solve(0, len(nums) - 1)

T(n) = 2T(n/2) + O(n) = O(n log n). It is slower than Kadane, so why care? Returning four values per segment (total, best prefix, best suffix, best overall) makes it O(n) and, more importantly, turns it into a segment tree node that answers "maximum subarray in range [l, r]" with point updates in O(log n). That is the real-world reason to know it; see segment trees.

How Kadane Shows Up in Interviews

Because the base problem is well known, interviewers mostly use it to test depth. A typical progression:

Level 1

Solve and explain

O(n) solution plus the "best ending here" explanation. Mention the all-negative case without being asked.

Level 2

Return indices / handle ties

Tests whether you understand when the run restarts, not just the max formula.

Level 3

Change the structure

Circular array, product instead of sum, one deletion. Tests transfer of the idea.

Level 4

Change the dimension

2D rectangle, range queries with updates. Tests composition with other techniques.

Red flags: using a sliding window (it does not work, because with negatives there is no monotonic condition to shrink on), returning 0 for all-negative input, or being unable to say why dropping a negative prefix is safe. Strong signals: naming the DP state, connecting it to Best Time to Buy and Sell Stock, and testing on an all-negative array before the interviewer does.

Common Kadane Mistakes

  • Initialising best to 0 when subarrays must be non-empty.
  • Circular without the all-negative guard, returning 0.
  • Max product without tracking the minimum, failing on [-2, 3, -4].
  • Swapping after updating in max product: the swap must happen before computing new hi and lo.
  • Using the new keep in one-deletion when computing deleted; the order of updates matters.
  • Overflow: with n = 105 and values up to 109, sums exceed 32-bit. Use 64-bit accumulators when constraints are large (LeetCode 53's bounds fit in int, but OAs vary).
  • Subarray vs subsequence: if non-contiguous elements are allowed, the answer is just the sum of positives (or the max element if none), no Kadane needed.
✅
Practice set in orderLC 53 Maximum Subarray, LC 121 Best Time to Buy and Sell Stock, LC 152 Maximum Product Subarray, LC 918 Maximum Sum Circular Subarray, LC 1749 Maximum Absolute Sum, LC 1186 One Deletion, then the 2D rectangle and LC 363. For more array patterns that sit next to Kadane, see array interview problems and patterns.

Frequently Asked Questions

What is Kadane's algorithm?▾
Kadane's algorithm finds the maximum sum of a contiguous subarray in O(n) time and O(1) space. It scans the array once, keeping the best sum of a subarray that ends at the current index: either extend the previous subarray or start fresh at the current element, whichever is larger. The answer is the largest of these values seen during the scan.
Does Kadane's algorithm work when all numbers are negative?▾
Yes, if you initialise both the current and best sums with the first element and use cur = max(x, cur + x). Then the answer is the largest single element. The version that resets the running sum to 0 and starts best at 0 returns 0 for an all-negative array, which is wrong when the subarray must be non-empty.
Is Kadane's algorithm dynamic programming or greedy?▾
It is best understood as dynamic programming with the state compressed to one variable: dp[i] is the maximum sum of a subarray ending at index i, and dp[i] = max(nums[i], dp[i-1] + nums[i]). It can also be read greedily, since you drop the running prefix whenever it becomes negative, but the DP view makes the correctness proof and the variants easier.
How do I find the maximum sum circular subarray?▾
The best circular subarray either does not wrap, which is plain Kadane, or wraps around, in which case it equals the total sum minus the minimum-sum subarray. Take the larger of the two. If every element is negative, the minimum subarray is the whole array and the wrap formula gives an empty subarray, so return the plain Kadane result instead.
Why does maximum product subarray track both maximum and minimum?▾
Because multiplying by a negative number turns the smallest (most negative) product into the largest and vice versa. Keeping both the maximum and minimum product ending at each index, and swapping them when the current element is negative, lets one pass handle sign flips. Zeros reset both naturally because max(x, ...) and min(x, ...) restart at the element.
What is the time complexity of the 2D maximum sum rectangle?▾
Fixing a top row and a bottom row and running Kadane on the column sums between them costs O(R^2 * C) for an R by C matrix. Transposing so that R is the smaller dimension gives O(min(R,C)^2 * max(R,C)). Brute force over all rectangles with prefix sums is O(R^2 * C^2).
Which companies ask Kadane's algorithm?▾
Maximum subarray and its variants are commonly reported across Indian service and product company interviews, online assessments and campus rounds, and at FAANG companies usually as a warm-up or inside a harder problem. The circular and product variants are the usual follow-ups once the base version is solved quickly.

From Warm-Up to Hard Variant

Prepflix's DSA program trains you to turn one core idea like Kadane into the circular, product and 2D follow-ups interviewers use to separate strong candidates.

Explore the Prepflix Course →
Pranjal Jain
Pranjal Jain

Ex-Microsoft SDE · IIT Kanpur · Founder of Prepflix. Helps engineers crack startup and product company interviews across India.