Array · 10 of 36
Kadanes Algo
Walk down a street where some houses give you candy and some take it away, and find the stretch of houses in a row that leaves you with the most candy.
- 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 whole numbers. Some are positive, some are negative. Pick a stretch of numbers that sit next to each other, with at least one number in it, so that their sum is as big as possible. Give back that sum.
A stretch must be in one piece. You may not skip a number in the middle. A piece like this is called a subarray.
A story to remember it
Picture it
Nine houses. The best stretch is 4, -1, 2, 1 at index 3 to 6. Its sum is 6. The -1 inside is worth keeping because 4 in front of it pays for it.
- answer
Examples
A normal case
- In
[-2, 1, -3, 4, -1, 2, 1, -5, 4]- Out
6
The stretch 4, -1, 2, 1 adds up to 6. No other stretch beats it.
Every number is negative
- In
[-3, -1, -2]- Out
-1
You must take at least one number. The least bad one is -1 on its own. Code that starts the best at 0 gives 0 here, which is wrong.
Every number is positive
- In
[1, 2, 3]- Out
6
Take the whole row. Adding a positive number always helps.
One number
- In
[5]- Out
5
The only stretch is the number itself.
A dip worth crossing
- In
[5, -9, 6, -2, 3]- Out
7
6, -2, 3 makes 7. Starting at 5 is a trap: 5 - 9 = -4 drags everything after it down, so we drop that bag.
Way 1: the simple way
Try every starting house. From each start, walk to the right, keep a running sum, and remember the biggest sum you see.
- For each start
i, setsum = 0. - For each end
jfromito the last house, addnums[j]tosum. - After each add, compare
sumwith the best and keep the bigger one.
class Solution {
public int maxSubArray(int[] nums) {
int best = Integer.MIN_VALUE;
for (int i = 0; i < nums.length; i++) { // where the walk starts
int sum = 0;
for (int j = i; j < nums.length; j++) { // where the walk stops
sum += nums[j];
best = Math.max(best, sum);
}
}
return best;
}
}How long it takes: there are about n * n / 2 pairs of start and end, so O(n^2). Extra room: two counters, O(1). Adding as we go (instead of summing each stretch from scratch, which would be O(n^3)) is already a small win.
Way 2: the clever way
Walk the street once, carrying one bag. At each house ask one question: is my bag worth keeping?
bagis the best sum of a stretch that ends at the current house.bestis the biggest bag ever.- At house
i: ifbagis below zero, throw it away and start fresh withnums[i]. Otherwise addnums[i]to the bag. - Update
bestif the bag is bigger.
Why is throwing away a negative bag always right? Any stretch that ends here and starts with that negative piece would be bigger without it. So the best stretch ending here either starts fresh or keeps a bag that is zero or more. This rule is Kadane's algorithm. The green boxes below show the best stretch found so far.
One bag, one walk
01 / 10
Start at the first house. The bag holds -2 and the best so far is -2.
- bag
- -2
- best
- -2
- looking now
- done
- answer
class Solution {
public int maxSubArray(int[] nums) {
int bag = nums[0]; // best sum of a stretch that ends here
int best = nums[0]; // best sum seen anywhere
for (int i = 1; i < nums.length; i++) {
if (bag < 0) bag = nums[i]; // a negative bag only hurts: drop it
else bag += nums[i]; // otherwise keep walking with it
best = Math.max(best, bag);
}
return best;
}
// Same walk, but also remember where the best stretch starts and ends.
public int[] bestStretch(int[] nums) {
int bag = nums[0], best = nums[0];
int start = 0, bestStart = 0, bestEnd = 0;
for (int i = 1; i < nums.length; i++) {
if (bag < 0) { bag = nums[i]; start = i; }
else bag += nums[i];
if (bag > best) { best = bag; bestStart = start; bestEnd = i; }
}
return new int[] { bestStart, bestEnd };
}
}How long it takes: one walk down the street, O(n). Extra room: two numbers, O(1).
Watch out for
- Some versions of the problem allow an empty stretch. Then the answer is never below 0, and starting at 0 is correct. Ask which version you have.
- If the numbers are huge, the sum can overflow an
int. Uselongwhen the problem hints at big values. - Do not confuse this with "maximum sum of any numbers you like". Skipping is not allowed; the stretch is in one piece.
What interviewers ask next
- Print the stretch, not only the sum. Remember where the current bag started, and copy it into
bestStartandbestEndwhenever the best improves, as inbestStretchabove. - The street is a circle. The best stretch either does not wrap (plain Kadane) or wraps, which equals the total minus the worst stretch in the middle (Kadane looking for the minimum). Take the bigger, but if every number is negative, use plain Kadane.
- Maximum product of a stretch. Keep both the biggest and the smallest product ending here, because a negative times a negative becomes big.
- Biggest sum of a rectangle in a 2D grid. Fix a top row and a bottom row, add up each column between them, and run Kadane on those column sums. That is
O(rows^2 * cols). - Can you do it with divide and conquer? Yes: the best stretch is in the left half, the right half, or crosses the middle. That is
O(n log n), slower, but it is a common follow up.
Remember this
Grown-up words: the stretch of houses is a subarray, the bag is the best sum ending at index i, and dropping a negative bag is Kadane's algorithm, a tiny 1D dynamic programming with dp[i] = max(nums[i], dp[i-1] + nums[i]).