Skip to content

Commit 3fc56ab

Browse files
williamfisetclaude
andauthored
Refactor MaximumSubarray (Kadane's algorithm) and add tests (williamfiset#1281)
- Add file-level header explaining Kadane's algorithm with recurrence - Add Javadoc, fix internal types to long for overflow safety - Replace if/else with Math.max, fix typo, move main to bottom - Change null/empty to throw instead of returning 0 - Add 15 tests covering edge cases, all-negative, overflow, and more Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
1 parent ca0a498 commit 3fc56ab

3 files changed

Lines changed: 154 additions & 22 deletions

File tree

Lines changed: 49 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,37 +1,64 @@
1+
package com.williamfiset.algorithms.dp;
2+
13
/**
2-
* This file shows you how to find the maximal subarray in an integer array Time complexity: O(n)
4+
* Maximum Subarray Problem (Kadane's Algorithm)
5+
*
6+
* Given an integer array, find the contiguous subarray with the largest sum.
7+
* Kadane's algorithm solves this in a single pass by maintaining the best
8+
* sum ending at the current position:
9+
*
10+
* sum[i] = max(arr[i], sum[i-1] + arr[i])
11+
*
12+
* At each index, we either extend the current subarray or start a new one
13+
* from the current element — whichever gives the larger sum. The global
14+
* maximum across all positions is the answer.
15+
*
16+
* Tested against: https://leetcode.com/problems/maximum-subarray
17+
*
18+
* Time: O(n)
19+
* Space: O(1)
320
*
421
* @author William Fiset, william.alexandre.fiset@gmail.com
522
*/
6-
package com.williamfiset.algorithms.dp;
7-
823
public class MaximumSubarray {
924

10-
public static void main(String[] args) {
11-
System.out.println(maximumSubarrayValue(new int[] {-5}));
12-
System.out.println(maximumSubarrayValue(new int[] {-5, -4, -10, -3, -1, -12, -6}));
13-
System.out.println(maximumSubarrayValue(new int[] {1, 2, 1, -7, 2, -1, 40, -89}));
14-
}
25+
/**
26+
* Returns the sum of the maximum contiguous subarray.
27+
*
28+
* @param arr the input array (must be non-null and non-empty)
29+
* @return the maximum subarray sum
30+
*
31+
* Time: O(n)
32+
* Space: O(1)
33+
*/
34+
public static long maximumSubarrayValue(int[] arr) {
35+
if (arr == null || arr.length == 0)
36+
throw new IllegalArgumentException("Array must not be null or empty");
1537

16-
// Return the value of the maximum subarray in 'ar'
17-
public static long maximumSubarrayValue(int[] ar) {
38+
long maxValue = arr[0];
39+
long sum = arr[0];
1840

19-
if (ar == null || ar.length == 0) return 0L;
20-
int n = ar.length, maxValue, sum;
41+
for (int i = 1; i < arr.length; i++) {
42+
// Either start a new subarray at arr[i], or extend the current one
43+
sum = Math.max(arr[i], sum + arr[i]);
2144

22-
maxValue = sum = ar[0];
45+
if (sum > maxValue)
46+
maxValue = sum;
47+
}
2348

24-
for (int i = 1; i < n; i++) {
49+
return maxValue;
50+
}
2551

26-
// At each step consider continuing the current subarray
27-
// or starting a new one because adding the next element
28-
// doesn't acutally make the subarray sum any better.
29-
if (ar[i] > sum + ar[i]) sum = ar[i];
30-
else sum = sum + ar[i];
52+
public static void main(String[] args) {
53+
// Single negative element
54+
System.out.println(maximumSubarrayValue(new int[] {-5})); // -5
3155

32-
if (sum > maxValue) maxValue = sum;
33-
}
56+
// All negative: best subarray is the largest single element (-1)
57+
System.out.println(
58+
maximumSubarrayValue(new int[] {-5, -4, -10, -3, -1, -12, -6})); // -1
3459

35-
return maxValue;
60+
// Mixed: subarray [2, -1, 40] has sum 41
61+
System.out.println(
62+
maximumSubarrayValue(new int[] {1, 2, 1, -7, 2, -1, 40, -89})); // 41
3663
}
3764
}

src/test/java/com/williamfiset/algorithms/dp/BUILD

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,5 +61,16 @@ java_test(
6161
deps = TEST_DEPS,
6262
)
6363

64+
# bazel test //src/test/java/com/williamfiset/algorithms/dp:MaximumSubarrayTest
65+
java_test(
66+
name = "MaximumSubarrayTest",
67+
srcs = ["MaximumSubarrayTest.java"],
68+
main_class = "org.junit.platform.console.ConsoleLauncher",
69+
use_testrunner = False,
70+
args = ["--select-class=com.williamfiset.algorithms.dp.MaximumSubarrayTest"],
71+
runtime_deps = JUNIT5_RUNTIME_DEPS,
72+
deps = TEST_DEPS,
73+
)
74+
6475
# Run all tests
6576
# bazel test //src/test/java/com/williamfiset/algorithms/dp:all
Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
package com.williamfiset.algorithms.dp;
2+
3+
import static com.google.common.truth.Truth.assertThat;
4+
import static org.junit.jupiter.api.Assertions.assertThrows;
5+
6+
import org.junit.jupiter.api.Test;
7+
8+
public class MaximumSubarrayTest {
9+
10+
@Test
11+
public void testNullInput() {
12+
assertThrows(
13+
IllegalArgumentException.class, () -> MaximumSubarray.maximumSubarrayValue(null));
14+
}
15+
16+
@Test
17+
public void testEmptyInput() {
18+
assertThrows(
19+
IllegalArgumentException.class, () -> MaximumSubarray.maximumSubarrayValue(new int[] {}));
20+
}
21+
22+
@Test
23+
public void testSinglePositive() {
24+
assertThat(MaximumSubarray.maximumSubarrayValue(new int[] {7})).isEqualTo(7);
25+
}
26+
27+
@Test
28+
public void testSingleNegative() {
29+
assertThat(MaximumSubarray.maximumSubarrayValue(new int[] {-5})).isEqualTo(-5);
30+
}
31+
32+
@Test
33+
public void testSingleZero() {
34+
assertThat(MaximumSubarray.maximumSubarrayValue(new int[] {0})).isEqualTo(0);
35+
}
36+
37+
@Test
38+
public void testAllPositive() {
39+
// Entire array is the max subarray
40+
assertThat(MaximumSubarray.maximumSubarrayValue(new int[] {1, 2, 3, 4})).isEqualTo(10);
41+
}
42+
43+
/** All negative: the max subarray is the single largest element. */
44+
@Test
45+
public void testAllNegative() {
46+
assertThat(MaximumSubarray.maximumSubarrayValue(
47+
new int[] {-5, -4, -10, -3, -1, -12, -6})).isEqualTo(-1);
48+
}
49+
50+
@Test
51+
public void testMixedWithNegativeReset() {
52+
// [2, -1, 40] = 41
53+
assertThat(MaximumSubarray.maximumSubarrayValue(
54+
new int[] {1, 2, 1, -7, 2, -1, 40, -89})).isEqualTo(41);
55+
}
56+
57+
@Test
58+
public void testMaxSubarrayAtStart() {
59+
// [5, 4] = 9
60+
assertThat(MaximumSubarray.maximumSubarrayValue(new int[] {5, 4, -20, 1, 2})).isEqualTo(9);
61+
}
62+
63+
@Test
64+
public void testMaxSubarrayAtEnd() {
65+
// [3, 7] = 10
66+
assertThat(MaximumSubarray.maximumSubarrayValue(new int[] {1, -20, 3, 7})).isEqualTo(10);
67+
}
68+
69+
@Test
70+
public void testMaxSubarrayInMiddle() {
71+
// [4, -1, 5] = 8
72+
assertThat(MaximumSubarray.maximumSubarrayValue(
73+
new int[] {-3, 4, -1, 5, -10})).isEqualTo(8);
74+
}
75+
76+
@Test
77+
public void testEntireArrayIsMax() {
78+
assertThat(MaximumSubarray.maximumSubarrayValue(
79+
new int[] {2, -1, 3, -1, 2})).isEqualTo(5);
80+
}
81+
82+
@Test
83+
public void testAllZeros() {
84+
assertThat(MaximumSubarray.maximumSubarrayValue(new int[] {0, 0, 0})).isEqualTo(0);
85+
}
86+
87+
@Test
88+
public void testLargeValues() {
89+
// Ensure long return type handles values beyond int range
90+
assertThat(MaximumSubarray.maximumSubarrayValue(
91+
new int[] {Integer.MAX_VALUE, Integer.MAX_VALUE}))
92+
.isEqualTo(2L * Integer.MAX_VALUE);
93+
}
94+
}

0 commit comments

Comments
 (0)