Maximum Subarray in Java DSA: Brute Force to Kadane’s Algorithm
-
Last Updated: July 13, 2026
-
By: javahandson
-
Series
Learn Java in a easy way
Learn the Maximum Subarray in Java DSA with a clear walkthrough: brute force plus Kadane’s Algorithm, dry run, and complexity comparison.
The Maximum Subarray problem in Java is a small question with a big payoff. It looks tricky at first. But once you meet Kadane’s Algorithm, the whole thing clicks, and you carry that trick into many harder DSA problems.
Here is the task in plain words. You get an array of numbers, and some may be negative. You must find the contiguous slice with the largest sum. The slice has to be one unbroken run of elements, so you cannot skip around.
In our last article we solved Contains Duplicate with a HashSet. Now we shift from checking membership to tracking a running total. In this guide we start with brute force, then trim it down, and finally reach Kadane’s clean one-pass solution. We also dry run the code by hand so you can watch the numbers move.
Let us pin down the rules first. A clear problem statement keeps you out of trouble later.
For that sample array, the answer is 6. It comes from the slice [4, -1, 2, 1]. Notice we return the sum here, not the slice itself. Many beginners start hunting for start and end indices, but the classic version only wants the value.
| 💡 Interview Insight Interviewers often ask why an all-negative array like [-3, -1, -2] still has an answer. Because a subarray needs at least one element, the best you can do is the single largest value, which is -1. Naming this edge case early shows you read the constraints carefully. |
Two small ideas carry this whole problem. Both are easy, and both show up again in later array questions.
A running sum is just a total you keep updating as you walk the array. At each number you make one choice: extend the current run, or start fresh from here. That single decision is the heart of the whole solution.
The second idea is even simpler. While the running sum moves up and down, you keep a separate variable that remembers the best total you have ever seen. So even if the run dips later, your best value stays safe.
The first idea most people reach for is direct. Try every possible subarray, add up each one, and keep the largest sum you find.
best = -infinity
for start from 0 to n-1:
sum = 0
for end from start to n-1:
sum = sum + nums[end]
best = max(best, sum)
return bestThe outer loop fixes a starting point. The inner loop stretches the slice one element at a time and updates the running sum. We compare that sum against our best after each step.
public class MaximumSubarrayBrute {
public static int maxSubArray(int[] nums) {
int best = Integer.MIN_VALUE;
for (int start = 0; start < nums.length; start++) {
int sum = 0;
for (int end = start; end < nums.length; end++) {
sum += nums[end];
best = Math.max(best, sum);
}
}
return best;
}
public static void main(String[] args) {
int[] nums = { -2, 1, -3, 4, -1, 2, 1 };
System.out.println(maxSubArray(nums)); // 6
}
}Let us go slow and picture what the code is really doing. A subarray is just a slice of the array with a left edge and a right edge. Brute force says: try every left edge, and for each one, try every right edge. That covers all possible slices.
Think of it like this. You put your left finger on one number. Then you drag your right finger across the array, adding up numbers as you go. Every time you move the right finger, you have a new slice, so you check its total.
Now let us tie each line to that picture.
Here is the key idea that trips people up. We do not build a fresh sum from scratch for every slice. Instead, as the right finger moves one step, we just add one more number to the total we already had. That small reuse saves a lot of repeated adding.
This version is easy to trust because it literally checks everything. Nothing is skipped, so it cannot miss the answer. Its only weakness is speed, which we fix next.
Let us count the work in plain terms. The left finger can sit on any of the n numbers. For each of those spots, the right finger walks across the rest of the array, which is again up to n steps. So the total work is roughly n times n.
In Big-O terms, that is O(n squared) time. If the array has 10 numbers, that is around 100 steps. If it has 1000 numbers, it jumps to around a million steps. You can see how fast this grows, which is why brute force drags on big inputs.
The space cost is much friendlier. We only keep two extra variables, sum and best, no matter how large the array is. So the space is O(1), meaning constant. For small arrays this whole approach is perfectly fine. For large ones, we need something faster.
Now for the version interviewers really want. Kadane’s Algorithm walks the array just once. Instead of rebuilding every slice, it carries one running total and makes a smart choice at each number. To really get it, let us build up to the final code in two steps.
Imagine you are walking along the array, adding numbers to a running total as you go. At some point that total might dip below zero. When it does, dragging it forward only hurts you. A negative total can never help a future slice, so you may as well throw it away and start again.
Here is a money analogy that makes it click. Say you are saving money each day, but some days you lose money. If your savings ever go negative, it is smarter to reset to zero and start saving again, rather than carry that debt forward. Kadane’s does exactly this with array sums.
So the plan has two moving parts. We keep a running total for the slice we are building, and we reset it to zero the moment it turns negative. Alongside that, we keep a separate best value that remembers the highest total we have ever reached.
Let us write that plan out literally, with a plain if check for the reset. This version matches the analogy word for word, so it is the easiest one to read.
public class MaximumSubarrayReset {
public static int maxSubArray(int[] nums) {
int currentSum = 0;
int bestMax = nums[0];
for (int i = 0; i < nums.length; i++) {
currentSum += nums[i]; // add the current number
bestMax = Math.max(bestMax, currentSum);
if (currentSum < 0) { // total turned negative
currentSum = 0; // throw it away, start fresh
}
}
return bestMax;
}
public static void main(String[] args) {
int[] nums = { -2, 1, -3, 4, -1, 2, 1 };
System.out.println(maxSubArray(nums)); // 6
}
}Read it slowly and it mirrors the savings story exactly.
The order of the last two steps matters. We record the best first, then reset. If you reset before recording, you could wipe out a total you never got credit for. So always update bestMax, then check for the reset.
Let us trace this version by hand on nums = [-2, 1, -3, 4, -1, 2, 1]. Watch currentSum climb, then snap back to zero whenever it turns negative. We start with currentSum = 0 and bestMax = -2.
| i | num | currentSum += num | bestMax | Negative? Reset? |
|---|---|---|---|---|
| 0 | -2 | 0 + -2 = -2 | -2 | Yes, reset to 0 |
| 1 | 1 | 0 + 1 = 1 | 1 | No |
| 2 | -3 | 1 + -3 = -2 | 1 | Yes, reset to 0 |
| 3 | 4 | 0 + 4 = 4 | 4 | No |
| 4 | -1 | 4 + -1 = 3 | 4 | No |
| 5 | 2 | 3 + 2 = 5 | 5 | No |
| 6 | 1 | 5 + 1 = 6 | 6 | No |
Notice step 2. The total drops to -2, so we reset to zero right after recording. That reset is why step 3 starts clean at 4 instead of dragging the -2 forward. From there the run climbs to 6, which is our answer.
The reset version is clear, but Kadane’s classic form folds that if check into a single line. Watch how the two ideas are really the same.
In the reset version, when currentSum is negative we throw it away, so the next number starts a run of just itself. When currentSum is positive, we keep it and add the next number on top. That is exactly a choice between two values: the number alone, or the number plus the running total.
We can write that choice in one line with Math.max. If the running total was negative, currentMax + nums[i] would be smaller than nums[i] alone, so Math.max picks nums[i] and effectively resets. If the running total was positive, currentMax + nums[i] wins, so we extend. The if check disappears into the max.
// The reset logic ... currentSum += nums[i]; if (currentSum < 0) currentSum = 0; // reset when negative // ... becomes this single line: currentMax = Math.max(nums[i], currentMax + nums[i]);
Both do the same job. The one-liner just decides restart-or-extend in one comparison, instead of adding first and cleaning up after. Here is the full compact version.
public class MaximumSubarrayKadane {
public static int maxSubArray(int[] nums) {
int currentMax = nums[0];
int bestMax = nums[0];
for (int i = 1; i < nums.length; i++) {
currentMax = Math.max(nums[i], currentMax + nums[i]);
bestMax = Math.max(bestMax, currentMax);
}
return bestMax;
}
public static void main(String[] args) {
int[] nums = { -2, 1, -3, 4, -1, 2, 1 };
System.out.println(maxSubArray(nums)); // 6
}
}Now let us walk through the final version slowly. We keep two variables, and it helps to know the job of each one before we start.
With those two roles clear, the lines make sense.
Notice why we need two separate variables and not one. The current run can rise, fall, and even reset to a small number. If we tracked only that, we would lose the big total we hit earlier. bestMax acts like a photograph of our best moment, safe from whatever the run does later.
Because we touch each number exactly once and do only a couple of comparisons per number, the work grows in a straight line with the array size. So the time is O(n) and the space is O(1). That single clean pass is what makes Kadane’s Algorithm shine.
| 💡 Interview Insight A common follow-up asks why we seed with nums[0] instead of 0. If you start best at 0, an all-negative array wrongly returns 0, since no run can beat an empty total. Seeding with the first element keeps the answer correct for every input. |
Now let us trace the compact Kadane’s version on the same array, nums = [-2, 1, -3, 4, -1, 2, 1]. It should reach the same answer as the reset version, just through the Math.max line instead of an if check. We follow both currentMax and bestMax as they change.
| i | num | currentMax = max(num, prev + num) | bestMax |
|---|---|---|---|
| 0 | -2 | -2 (start) | -2 |
| 1 | 1 | max(1, -2 + 1) = 1 | 1 |
| 2 | -3 | max(-3, 1 + -3) = -2 | 1 |
| 3 | 4 | max(4, -2 + 4) = 4 | 4 |
| 4 | -1 | max(-1, 4 + -1) = 3 | 4 |
| 5 | 2 | max(2, 3 + 2) = 5 | 5 |
| 6 | 1 | max(1, 5 + 1) = 6 | 6 |
Look at step 3 closely. The running total had dipped to -2, so adding 4 gives only 2. But 4 by itself is larger, so we restart the run at 4. Compare this to the reset version: there, currentSum had already snapped to 0, so 0 + 4 also gave 4. Both land on the same value, which shows the two versions really are the same idea.
From there the run climbs to 6, which becomes our final answer.

The sketch shows the same trace. The array sits on top, each step lists the choice, and the running values sit on the right. Many people find this picture easier than the table, so use whichever clicks for you.
Both approaches give the correct answer. They just pay different prices. Here is the plain comparison.
| Approach | Time | Space | Note |
|---|---|---|---|
| Brute force | O(n squared) | O(1) | Simple but slow on big inputs |
| Kadane’s | O(n) | O(1) | Fastest, single pass, no extra memory |
In an interview, mention brute force first to show you understand the problem. Then move to Kadane’s to show you can reach the best time complexity. That jump from slow to fast is exactly what interviewers want to see. Notice Kadane’s even wins on space, since it needs no extra array.
| 💡 Interview Insight If the interviewer asks you to also return the subarray itself, not just the sum, track the start and end indices alongside currentMax. Reset the tentative start whenever you restart the run. Mentioning this extension out loud signals real depth. |
A few small traps catch beginners on this problem. Keep these in mind.
A: You are given an array of integers, and some may be negative. The task is to find the contiguous subarray with the largest sum and return that sum. The slice must be one unbroken run, so you cannot skip elements.
A: Kadane’s walks the array once. At each number it picks the larger of the number alone or the number added to the running total, then updates a best-so-far value. This gives an O(n) time and O(1) space solution.
A: Kadane’s Algorithm runs in O(n) time because it makes a single pass through the array. It uses O(1) extra space, since it only tracks two running values.
A: Seeding with zero breaks all-negative arrays, since no run can beat an empty total, so the code wrongly returns zero. Seeding with the first element keeps the result correct for every input.
A: Yes. Track a tentative start index and reset it whenever the run restarts, then record start and end whenever bestMax improves. This returns the actual slice alongside the sum.
The Maximum Subarray problem in Java looks small, and it is. But Kadane’s Algorithm inside it is a real skill. You will reuse that extend-or-restart idea in many tougher array and dynamic programming problems.
So take the pattern, not just the answer. Try brute force first to be safe. Then reach for Kadane’s when you want a fast single pass. Once that decision feels natural, a whole family of problems opens up for you.
You now have Kadane’s Algorithm in your toolkit, so let us keep the momentum going. Here is where you came from and where you head next in this series.