Kadane's Algorithm
LeetCode ↗ Java source ↗ Edit this article ↗
Jump to code:
Problem
You are given a non-empty integer array nums. Return the largest possible sum of a contiguous, non-empty subarray. This is LeetCode’s “Maximum Subarray”, and the linear-time method that solves it is called Kadane’s algorithm.
A frequent variant allows the empty subarray, whose sum is 0; in that variant the answer is max(0, result). The code below implements the classic non-empty version, so an all-negative array returns its largest (least negative) element.
Constraints: 1 ≤ n ≤ 10^5, -10^9 ≤ nums[i] ≤ 10^9. With these bounds a sum can reach 10^14, which does not fit in an int, so the methods return long.
Example 1
nums = [-2, 1, -3, 4, -1, 2, 1, -5, 4]
answer = 6 // [4, -1, 2, 1]
Example 2
nums = [5, 4, -1, 7, 8]
answer = 23 // the whole array
Example 3
nums = [-3, -1, -2]
answer = -1 // every subarray is negative; the single element -1 is the best
Approach 1: Brute force
Intuition. Enumerate every subarray by its start and end, compute its sum with a third loop, and keep the maximum. It is the definition of the problem written as code.
Algorithm.
- For every start
iand every endj ≥ i: - Sum
nums[i..j]. - Keep the maximum sum seen.
static long bruteForce(int[] nums) {
int n = nums.length;
long best = Long.MIN_VALUE;
for (int i = 0; i < n; i++) {
for (int j = i; j < n; j++) {
long sum = 0;
for (int k = i; k <= j; k++) sum += nums[k];
best = Math.max(best, sum);
}
}
return best;
}
Complexity. Time O(n^3): O(n^2) subarrays, each summed in O(n). Space O(1).
Approach 2: Better, running sum per start
Intuition. The innermost loop recomputes almost the same sum over and over. For a fixed start i, the sum of nums[i..j] is the sum of nums[i..j-1] plus nums[j], so keep a running total as j advances. That removes one factor of n.
Algorithm.
- For every start
i: setsum = 0. - For every end
j ≥ i: addnums[j]tosumand update the maximum.
static long better(int[] nums) {
int n = nums.length;
long best = Long.MIN_VALUE;
for (int i = 0; i < n; i++) {
long sum = 0;
for (int j = i; j < n; j++) {
sum += nums[j];
best = Math.max(best, sum);
}
}
return best;
}
Complexity. Time O(n^2). Space O(1).
For n = 10^5 this is still about 5 × 10^9 additions. To go faster we must stop considering every start separately.
Approach 3: Optimal, Kadane’s algorithm
Intuition. Scan left to right, carrying current, the sum of the subarray that ends at the present position and that we are still willing to extend. When we reach element x we have a choice: extend the carried subarray (current + x) or start fresh at x. Extending is only worth it if the carried sum is positive; a negative prefix can only drag the total down, so it should be dropped. That is the whole algorithm: add x to current, record current as a candidate answer, and if current has become negative, reset it to 0 so that the next element starts a new subarray.
Why is dropping a negative prefix safe? Suppose the optimal subarray is nums[s..e]. Consider the moment the scan is at s. Either current was just reset to 0 (good, we start exactly at s) or current is non-negative and we extend it, which gives a sum ≥ the sum of nums[s..e] once we reach e; it cannot be strictly larger because nums[s..e] is optimal, so it is equal, and in both cases the answer is recorded when the scan passes e. Formally this is dynamic programming over “best subarray ending at i”: end[i] = max(nums[i], end[i-1] + nums[i]), and the answer is the maximum of all end[i].
Algorithm.
- Set
best = -∞,current = 0. - For every
x:current += x;best = max(best, current); ifcurrent < 0, setcurrent = 0. - Return
best.
static long optimal(int[] nums) {
long best = Long.MIN_VALUE, current = 0;
for (int x : nums) {
current += x;
best = Math.max(best, current);
if (current < 0) current = 0;
}
return best;
}
Dry run on nums = [-2, 1, -3, 4, -1, 2, 1, -5, 4]:
| x | current after add | best | reset? |
|---|---|---|---|
| -2 | -2 | -2 | yes → 0 |
| 1 | 1 | 1 | no |
| -3 | -2 | 1 | yes → 0 |
| 4 | 4 | 4 | no |
| -1 | 3 | 4 | no |
| 2 | 5 | 5 | no |
| 1 | 6 | 6 | no |
| -5 | 1 | 6 | no |
| 4 | 5 | 6 | no |
Complexity. Time O(n): one pass with constant work per element. Space O(1).
The order of the three statements inside the loop matters. best is updated before the reset, which is what makes the all-negative case work: for [-3, -1, -2] the values -3, -1, -2 are each recorded as candidates before current is cleared, so the answer is -1. Updating best after the reset would wrongly return 0, which is the answer to the “empty subarray allowed” variant instead.
The recurrence form
Many people remember Kadane’s algorithm as the DP recurrence directly. It is the same computation with the reset folded into a max:
static long optimalRecurrence(int[] nums) {
long best = nums[0], endingHere = nums[0];
for (int i = 1; i < nums.length; i++) {
endingHere = Math.max(nums[i], endingHere + nums[i]);
best = Math.max(best, endingHere);
}
return best;
}
endingHere is the best sum of a subarray that ends exactly at i. Choosing nums[i] alone over endingHere + nums[i] is precisely “drop the prefix when it is negative”. This version starts from nums[0], which is why it needs a non-empty array; the reset version uses Long.MIN_VALUE as the initial best and 0 as the initial carry, which amounts to the same thing.
Edge cases and pitfalls
- All negative numbers. The answer is the largest element, not
0. Record the candidate before resetting, or use the recurrence form. - Single element. Returns that element, positive or negative.
- Sums that return to zero (
[1, -1, 1, -1, 1]). The reset condition iscurrent < 0, strictly. A carried sum of exactly0neither helps nor hurts, so whether you reset on0or not does not change the maximum sum; it does change which subarray you would report, which matters in the follow-up problem that prints the subarray. - Overflow.
[Integer.MAX_VALUE, Integer.MAX_VALUE]must return4294967294. The running sum and the answer arelong;Math.maxresolves to thelongoverload automatically. - Empty array. Not part of the problem. The reset version would return
Long.MIN_VALUEand the recurrence version would throw onnums[0]; guard for it if your input may be empty. - Empty-subarray variant. If an empty subarray is allowed, return
Math.max(0, result); equivalently initialisebest = 0in the reset version.
Related problems
- Print subarray with maximum subarray sum (extended version of above problem) adds start/end bookkeeping to report the subarray itself.
- Maximum Product Subarray in an Array is the multiplicative cousin, where a negative “prefix” can become useful again.
- Stock Buy and Sell is Kadane’s algorithm in disguise: run it on the day-to-day price differences.
- Maximum Consecutive Ones is the simplest “carry a running quantity and reset” sweep.