Skip to content
dsa450for five year olds

Dynamic Programming · 35 of 60

Largest Sum Contiguous Subarray

Find the stretch of numbers next to each other that adds up to the biggest total.

Simple way
time O(n^2)
room O(1)
Clever way
time O(n)
room O(1)

The problem, simply

You get a row of numbers. Some are positive, some are negative. Pick a stretch of numbers that sit next to each other (at least one number) so that their sum is as big as possible. Give back that sum.

A stretch of neighbors is called a subarray. This is the maximum subarray problem, and the famous fast answer is Kadane's algorithm.

A story to remember it

Picture it

arrayarr
01234567sum 7-2-34-1-215-3

A row of numbers. The bracket marks the best stretch, 4, -1, -2, 1, 5, which adds up to 7.

  • answer

Examples

A normal case

In
arr = [-2, -3, 4, -1, -2, 1, 5, -3]
Out
7

The stretch 4, -1, -2, 1, 5 adds to 7. The dips in the middle are worth crossing to reach the 5.

All negative

In
arr = [-5, -1, -8]
Out
-1

The stretch must have at least one number. The least bad choice is -1 alone. Code that starts best at 0 wrongly returns 0.

All positive

In
arr = [2, 3, 1]
Out
6

Take everything.

One number

In
arr = [-7]
Out
-7

The only stretch is the number itself.

LeetCode's case

In
arr = [-2, 1, -3, 4, -1, 2, 1, -5, 4]
Out
6

The stretch 4, -1, 2, 1 adds to 6.

Way 1: the simple way

Try every starting house. From each start, walk right and keep a running sum. Every running sum is the sum of one stretch, so remember the biggest one you see.

  1. For each start from 0 to n - 1, set sum = 0.
  2. For each end from start to n - 1, add arr[end] to sum and update best.
arrayarr
01234567-2-34-1-215-3endstart
arrayrunning sum from start 2
01234567431274

Way 1 with start = 2. The stretch grows one number at a time: 4, then 3, then 1, then 2, then 7, then 4. The best seen from this start is 7.

  • checking
  • answer
java
class Solution {
    public int maxSubArray(int[] arr) {
        int best = Integer.MIN_VALUE;
        for (int start = 0; start < arr.length; start++) {
            int sum = 0;
            for (int end = start; end < arr.length; end++) {
                sum += arr[end];                 // the stretch start..end
                best = Math.max(best, sum);
            }
        }
        return best;
    }
}

How long it takes: about n * n / 2 stretches, O(n^2). Extra room: two counters, O(1).

Way 2: the clever way

Ask a smaller question at every house: "what is the best stretch that ends right here?" Call it cur. A stretch that ends here either is this number alone, or is the best stretch ending at the house before plus this number. So:

cur = max(arr[i], cur + arr[i])

In kid words: if the bag is negative, throw it away and start fresh. The answer is the biggest cur you ever see. You only need the one before, so one number is enough instead of a whole table.

  1. Set cur = best = arr[0].
  2. For each next number, set cur = max(arr[i], cur + arr[i]).
  3. Set best = max(best, cur).

The second row in the animation is the table dp[i] that cur stands for. The code never stores it.

Carry the run or start fresh

01 / 09

arrayarr
01234567current run-2-34-1-215-3i
arraybest sum ending here
01234567-2

Start with the first number alone. The best run ending here is -2, and the best run so far is also -2.

current
-2
best
-2
  • looking now
  • done
  • answer
  • just added
java
class Solution {
    public int maxSubArray(int[] arr) {
        int cur = arr[0];     // best stretch that ends at this house
        int best = arr[0];    // best stretch seen anywhere
        for (int i = 1; i < arr.length; i++) {
            cur = Math.max(arr[i], cur + arr[i]);   // start fresh or carry on
            best = Math.max(best, cur);
        }
        return best;
    }

    // Also report where the best stretch starts and ends.
    public int[] maxSubArrayWithRange(int[] arr) {
        int cur = arr[0], best = arr[0];
        int start = 0, bestStart = 0, bestEnd = 0;
        for (int i = 1; i < arr.length; i++) {
            if (arr[i] > cur + arr[i]) { cur = arr[i]; start = i; }  // fresh start here
            else cur += arr[i];
            if (cur > best) { best = cur; bestStart = start; bestEnd = i; }
        }
        return new int[] { best, bestStart, bestEnd };
    }
}

How long it takes: one look at each number, O(n). Extra room: two numbers, O(1).

Watch out for

  • Update best after updating cur, every step, not only when cur grows.
  • Big numbers: the sum can pass the int limit. Use long if values or lengths are large.
  • Empty input: decide with the interviewer. Usually the row has at least one number.
  • To return the stretch itself, remember where the current run started and copy it into the answer when best changes.

What interviewers ask next

  • Return the start and end index: see maxSubArrayWithRange.
  • The row is a circle (the end wraps to the start): the answer is the bigger of the normal answer and total - (smallest stretch). If all numbers are negative, use the normal answer.
  • Smallest sum stretch: same idea with min instead of max.
  • Biggest product stretch: keep both the biggest and the smallest product ending here, because a negative times a negative becomes big.
  • Biggest sum rectangle in a 2D grid: fix a top row and a bottom row, add up each column between them, and run Kadane on that row of column sums. That costs O(rows^2 * cols).
  • Can you do it with divide and conquer? Yes, in O(n log n): the best stretch is in the left half, the right half, or crosses the middle. Kadane is faster.

Remember this

Grown-up words: the stretch of houses is a contiguous subarray, the candy bag is the running sum cur, "best ending here" is the DP state, and the whole trick is Kadane's algorithm.