Kadane's Algorithm — All-Negative Input Returns $340k Loss
A Kadane's implementation seeded with 0 returns 0 for all-negative arrays, causing a $340k loss.
- Core decision: currentSum = max(arr[i], currentSum + arr[i])
- Tracks two values: currentSum (best ending here) and maxSumFound (global best)
- Time: O(n). Space: O(1). Single pass, two variables.
- Seed with arr[0], not 0 — otherwise all-negative arrays return wrong answer.
- Financial analytics: find peak profitability window in time-series data.
- Signal processing: detect strongest signal burst in noisy data.
- Seeding currentSum with 0. Returns 0 for [-3, -1, -2] instead of correct answer -1.
Imagine you're tracking your daily profit and loss at a lemonade stand over a month. Some days you make money, some days you lose it. You want to find the single best streak of consecutive days — the run where your total earnings were the highest. Kadane's Algorithm does exactly that: it walks through the numbers one by one, deciding at each step whether to extend the current streak or start fresh. It's like asking yourself every morning: 'Am I better off adding today to my running total, or cutting my losses and starting over from today?'
Maximum subarray sum — finding the contiguous segment of an array with the largest sum — appears in financial analytics (peak profitability windows), signal processing (strongest signal bursts), and genomics (highest-expression DNA regions). The naive approach checks every start-end pair: O(n²). For a 10,000-element array, that is 50 million operations. Kadane's Algorithm solves it in a single linear pass: O(n), O(1) space.
The algorithm maintains two values: currentSum (the best sum ending at the current position) and maxSumFound (the global best). At each element, it decides: extend the current subarray or start fresh. The decision is greedy and provably correct — a negative prefix can never improve a subarray's sum, so it is always discarded.
The common misconception is that this is 'just a DP problem.' While Kadane's recurrence (dp[i] = max(arr[i], dp[i-1] + arr[i])) is a DP formulation, the O(1) space optimization makes it a distinct pattern. Understanding the greedy intuition — not just the recurrence — is what separates candidates who can explain it on a whiteboard from those who just memorize the code.
How Kadane's Algorithm Works — Plain English
Kadane's algorithm finds the maximum-sum contiguous subarray in O(n) time and O(1) space. The key insight: at each position, either extend the previous subarray or start a fresh one — whichever gives a higher sum.
Step-by-step: 1. Initialize current_sum = arr[0], max_sum = arr[0]. 2. For i from 1 to n-1: a. current_sum = max(arr[i], current_sum + arr[i]). b. max_sum = max(max_sum, current_sum). 3. Return max_sum.
Worked example on [-2, 1, -3, 4, -1, 2, 1, -5, 4]: i=0: current=-2, max=-2. i=1: current=max(1,-2+1)=1. max=1. i=2: current=max(-3,1-3)=-2. max=1. i=3: current=max(4,-2+4)=4. max=4. i=4: current=max(-1,4-1)=3. max=4. i=5: current=max(2,3+2)=5. max=5. i=6: current=max(1,5+1)=6. max=6. i=7: current=max(-5,6-5)=1. max=6. i=8: current=max(4,1+4)=5. max=6. Maximum sum subarray = 6, from [4,-1,2,1].
- currentSum > 0: extend. The running sum boosts future elements.
- currentSum <= 0: restart. The running sum is dead weight.
- maxSumFound: tracks the global best across all positions.
- Each element is visited exactly once. O(n) time, O(1) space.
- The algorithm never backtracks. It makes the locally optimal choice at each step.
The Core Intuition — Why the Greedy Decision Works
Before touching code, let's lock in the mental model. At every index i, you face exactly one binary choice: does the current element join the existing subarray, or does it become the start of a brand-new one?
The rule is beautifully simple: if your running sum so far is negative, it's dead weight. Carrying it into the next element can only make that element's contribution worse. So you drop it. You set your running sum equal to the current element and start fresh. If your running sum is positive, it's a booster — keep it.
This is a greedy decision, and it's correct because the optimal subarray can never benefit from a negative prefix. Think about it: if the subarray [3, -5, 2, 4] were the optimal answer, you could always remove the [3, -5] prefix (which sums to -2) and get [2, 4] with a larger sum. That contradiction proves negative prefixes can never be part of the true maximum subarray.
The algorithm tracks two values at all times: currentSum (the best sum ending at the current position) and maxSumFound (the global best seen so far). Every step, you update both. When the loop finishes, maxSumFound is your answer.
- Seed with arr[0]: correct for all-negative, all-positive, and mixed arrays.
- Seed with 0: returns 0 for all-negative arrays. Wrong answer, no error.
- If the problem allows empty subarrays: seed with 0 is correct. Read the problem statement.
- If the problem requires non-empty subarrays: seed with arr[0]. This is the default.
- Test with [-3, -1, -2] to verify. Expected: -1. If you get 0, you seeded wrong.
Tracking the Actual Subarray — Not Just the Sum
The basic version tells you the maximum sum, but real-world use cases often need to know which subarray produced it. Imagine a financial dashboard that needs to highlight the exact date range of peak profitability — the number alone isn't enough.
Extending Kadane's to track indices takes almost no extra code. You record a potentialStart index whenever you decide to restart (i.e., when starting fresh beats extending). The moment that fresh start leads to a new global maximum, you commit it: you update bestStart, bestEnd, and maxSumFound together as an atomic snapshot.
This is a subtle but important pattern. You only want to commit the start index when you've confirmed it produced a better result, not optimistically when you reset. That distinction separates a correct implementation from a buggy one that snaps the wrong start index.
- potentialStart: updated on every reset. Tentative — may never become the answer.
- bestStart: updated only when currentSum > maxSumFound. Confirmed winner.
- Premature commit: setting bestStart = potentialStart on reset gives wrong boundaries.
- Lazy commit: setting bestStart = potentialStart only inside the max-update block is correct.
- Test with [1, 2, -10, 3, 7, -1, 8]. Expected: start=3, end=6, sum=17.
Kadane's vs. Brute Force vs. Divide and Conquer — Choosing Wisely
Understanding why Kadane's is the right choice requires knowing what you're giving up and getting compared to the alternatives.
The brute-force approach uses two nested loops to check every (start, end) pair. It's O(n²) and straightforward to reason about, which makes it fine for arrays under a few hundred elements in non-critical paths. Divide and Conquer solves the problem in O(n log n) — better than brute force, but still worse than Kadane's. Its advantage is that it's parallelizable: you can split the array across multiple cores and merge results, which matters in distributed systems processing massive datasets.
Kadane's wins on pure time complexity and simplicity for the single-machine case. One pass, two variables, done. The only scenario where you'd reach for Divide and Conquer over Kadane's is when you need to run this computation across distributed partitions where a central linear scan isn't possible — think MapReduce jobs over terabyte-scale log files.
For coding interviews, Kadane's is the expected answer the moment you see 'maximum subarray.' Mentioning the Divide and Conquer alternative shows depth and will genuinely impress a senior interviewer.
- Brute force: O(n^2). Simple. Fine for arrays under 1,000 elements.
- Divide and conquer: O(n log n). Parallelizable. Best for distributed systems.
- Kadane's: O(n). Simple. Best for single-machine linear scans.
- Interview expected answer: Kadane's. Mention D&C for bonus points.
- D&C advantage: split array across machines, compute max in each partition, merge by checking the crossing subarray.
Trading Algorithm Identified Wrong Peak Window: Seeded with Zero on All-Negative Returns
- Seeding Kadane's with 0 silently returns wrong answers on all-negative arrays. No exception, no error — just a wrong number that downstream systems trust.
- Always validate algorithm outputs against known edge cases. A 0 result on an all-negative input is a red flag.
- Financial systems must reject empty-subarray results. Add explicit validation: maxSumFound must correspond to a non-empty subarray.
- Unit tests for Kadane's must include: all-negative, single element, mixed, and all-positive arrays.
- Document the seeding requirement in code comments. Future developers will not know why arr[0] is used instead of 0.
Key takeaways
currentSum = Math.max(element, currentSum + element) — this single line encodes the entire greedy insight that a negative prefix can never help you.Interview Questions on This Topic
Frequently Asked Questions
That's Arrays & Strings. Mark it forged?
4 min read · try the examples if you haven't