Definition
- Kadane's algorithm finds the maximum sum of any contiguous non-empty subarray in a single linear pass.
- At each position, compute
currentMax = Math.max(nums[i], currentMax + nums[i]). - This captures the decision: extend the running sum or restart from the current element.
- Track
globalMax = Math.max(globalMax, currentMax)to remember the best. - Seed both variables from the first element and iterate from the second.
- Time is O(N) and space is O(1).
At the National Bank of India, Rajesh reviews daily profit-and-loss figures for a trading desk, some positive gains and some negative losses. He wants the most profitable continuous stretch of days. As he walks day by day, he asks: is my running streak still helping me, or should I abandon it and start fresh from today? If the streak has gone negative, it is dead weight, so he drops it and starts over, always remembering the best streak total.
Understanding the Concepts
What is the Maximum Subarray Sum problem?
Given an array of integers that may include negative numbers, find the maximum sum obtainable from any contiguous non-empty subarray. This is one of the most fundamental dynamic programming problems, solved optimally by Kadane's algorithm in linear time.
For example, given [-2, 1, -3, 4, -1, 2, 1, -5, 4], the maximum subarray sum is 6, from the subarray [4, -1, 2, 1].
How does Kadane's algorithm work?
The insight is that the maximum subarray must end at some position. If for every position we compute the maximum sum of a subarray ending exactly there, the overall answer is the largest of those per-position values.
package com.bank.service;
public class MaxSubarraySumService {
/**
* Finds the most profitable continuous trading stretch.
* Used by the National Bank of India risk management team.
*/
public static long maxSubarraySum(long[] profitLoss) {
long currentMax = profitLoss[0];
long globalMax = profitLoss[0];
for (int i = 1; i < profitLoss.length; i++) {
currentMax = Math.max(profitLoss[i],
currentMax + profitLoss[i]);
globalMax = Math.max(globalMax, currentMax);
}
return globalMax;
}
public static void main(String[] args) {
long[] profitLoss = {-20000, 10000, -30000, 40000,
-10000, 20000, 10000, -50000, 40000};
System.out.println("Best continuous P&L: Rs." + maxSubarraySum(profitLoss));
}
}
javac -d out com/bank/service/MaxSubarraySumService.java
java -cp out com.bank.service.MaxSubarraySumService
Best continuous P&L: Rs.60000
Why must you seed from the first element?
Because the subarray must be non-empty and the array may be all negative, you must start both currentMax and globalMax at nums[0] and iterate from index 1. Starting globalMax at zero would wrongly report zero for an all-negative array, when the correct answer is the single least-negative element.
// WRONG: seeds globalMax at 0
long globalMax = 0; // Returns 0 for [-5, -3, -1] instead of -1
// CORRECT: seeds from first element
long globalMax = profitLoss[0]; // Returns -1 for [-5, -3, -1]
How do you recover the subarray indices?
To find the actual start and end indices of the best subarray, track a tentative start that resets whenever you choose to restart, and commit the best start and end whenever the global maximum improves.
package com.bank.service;
public class MaxSubarrayWithIndices {
public static void findBestStretch(long[] pnl) {
long currentMax = pnl[0], globalMax = pnl[0];
int tempStart = 0, bestStart = 0, bestEnd = 0;
for (int i = 1; i < pnl.length; i++) {
if (pnl[i] > currentMax + pnl[i]) {
currentMax = pnl[i];
tempStart = i; // Restart from here
} else {
currentMax = currentMax + pnl[i];
}
if (currentMax > globalMax) {
globalMax = currentMax;
bestStart = tempStart;
bestEnd = i;
}
}
System.out.println("Best stretch: day " + bestStart
+ " to day " + bestEnd + ", total: Rs." + globalMax);
}
public static void main(String[] args) {
long[] pnl = {-20000, 10000, -30000, 40000,
-10000, 20000, 10000, -50000, 40000};
findBestStretch(pnl);
}
}
Best stretch: day 3 to day 6, total: Rs.60000
What is the recurrence behind Kadane's?
Define f(i) as the maximum sum of a subarray ending at index i. The recurrence is:
f(0) = nums[0]f(i) = max(nums[i], f(i-1) + nums[i])for i > 0
The answer is max(f(i)) over all i. Kadane's algorithm computes this in O(1) space by keeping only the previous f value.
What are the edge cases?
Several edge cases require attention.
- All negative: the answer is the single least-negative element.
- Single element: the answer is that element.
- All positive: the answer is the sum of the entire array.
- Large values: use
longto prevent integer overflow on financial data.
package com.bank.core;
public class KadaneEdgeCases {
public static void main(String[] args) {
System.out.println(maxSum(new long[]{-5, -3, -1})); // -1
System.out.println(maxSum(new long[]{42})); // 42
System.out.println(maxSum(new long[]{1, 2, 3, 4})); // 10
}
static long maxSum(long[] arr) {
long cur = arr[0], best = arr[0];
for (int i = 1; i < arr.length; i++) {
cur = Math.max(arr[i], cur + arr[i]);
best = Math.max(best, cur);
}
return best;
}
}
-1
42
10
What is the time and space complexity?
Time is O(N): a single pass through the array. Space is O(1): two scalar variables. This is optimal because you must read every element at least once.
What should a senior engineer know?
A senior engineer should understand these deeper aspects.
- DP collapsed to O(1) space: Kadane's is the canonical example of sequence DP where the state depends only on the previous position, so the full DP table collapses to a single variable.
- Divide and conquer alternative: the maximum subarray can also be found in O(N log N) using divide and conquer, but Kadane's O(N) dominates.
- Prefix sum view: the maximum subarray sum equals the maximum of
prefix[i] - prefix[j]for j < i, which is equivalent to tracking the running minimum prefix sum. - Overflow: for financial P&L data, always accumulate in
longorBigDecimal.
What are common interview follow-up questions?
Interviewers often extend this problem.
- Minimum subarray sum: flip the comparison to Math.min (Kadane's mirror).
- Maximum product subarray: track both max and min products.
- Maximum subarray sum with one deletion: add a second DP state.
- Maximum sum circular subarray: combine max and min Kadane.
Visual Overview
P&L: [-20, 10, -30, 40, -10, 20, 10, -50, 40] (thousands) ┌──────────────────────────────────────────────┐ │ i=0: cur=-20, best=-20 │ │ i=1: max(10, -20+10)=10, cur=10, best=10 │ │ i=2: max(-30, 10-30)=-20, cur=-20, best=10 │ │ i=3: max(40, -20+40)=40, cur=40, best=40 │ │ i=4: max(-10, 40-10)=30, cur=30, best=40 │ │ i=5: max(20, 30+20)=50, cur=50, best=50 │ │ i=6: max(10, 50+10)=60, cur=60, best=60 │ │ i=7: max(-50, 60-50)=10, cur=10, best=60 │ │ i=8: max(40, 10+40)=50, cur=50, best=60 │ └──────────────────────────────────────────────┘ Answer: 60 (subarray from index 3 to 6)
Key Takeaway
The maximum subarray sum problem asks for the largest sum of any contiguous non-empty subarray of an array that may contain negatives, and Kadane's algorithm solves it in a single linear pass. The insight is that the best subarray must end somewhere, so if you compute the best sum ending at each position, the overall answer is the largest of those values. At each element, you choose whether to extend the running sum or restart from the current element, whichever is larger.
The initialization detail is critical: seed both currentMax and globalMax from the first element and iterate from the second. If you lazily start globalMax at zero, you get the wrong answer for all-negative arrays. This seeding is where most buggy implementations fail.
At the National Bank of India, Rajesh used this on the proprietary trading desk. Daily profit and loss arrived as signed amounts, and risk management needed the single most profitable continuous run of days for performance attribution. The index-tracking extension identified the exact date range of the best stretch, which went directly into the nightly report.
The senior-level point is that Kadane's algorithm is sequence DP collapsed to O(1) space, because the state depends only on the previous position. The recurrence, the seeding pitfall, the index recovery, and the overflow safeguard are the four corners of a complete answer.
Remember one sentence: at each element, take the max of the element alone or extending the running sum, track the global best, and seed from the first element.
---