Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
71 changes: 49 additions & 22 deletions src/main/java/com/williamfiset/algorithms/dp/MaximumSubarray.java
Original file line number Diff line number Diff line change
@@ -1,37 +1,64 @@
package com.williamfiset.algorithms.dp;

/**
* This file shows you how to find the maximal subarray in an integer array Time complexity: O(n)
* Maximum Subarray Problem (Kadane's Algorithm)
*
* Given an integer array, find the contiguous subarray with the largest sum.
* Kadane's algorithm solves this in a single pass by maintaining the best
* sum ending at the current position:
*
* sum[i] = max(arr[i], sum[i-1] + arr[i])
*
* At each index, we either extend the current subarray or start a new one
* from the current element — whichever gives the larger sum. The global
* maximum across all positions is the answer.
*
* Tested against: https://leetcode.com/problems/maximum-subarray
*
* Time: O(n)
* Space: O(1)
*
* @author William Fiset, william.alexandre.fiset@gmail.com
*/
package com.williamfiset.algorithms.dp;

public class MaximumSubarray {

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

// Return the value of the maximum subarray in 'ar'
public static long maximumSubarrayValue(int[] ar) {
long maxValue = arr[0];
long sum = arr[0];

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

maxValue = sum = ar[0];
if (sum > maxValue)
maxValue = sum;
}

for (int i = 1; i < n; i++) {
return maxValue;
}

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

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

return maxValue;
// Mixed: subarray [2, -1, 40] has sum 41
System.out.println(
maximumSubarrayValue(new int[] {1, 2, 1, -7, 2, -1, 40, -89})); // 41
}
}
11 changes: 11 additions & 0 deletions src/test/java/com/williamfiset/algorithms/dp/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -61,5 +61,16 @@ java_test(
deps = TEST_DEPS,
)

# bazel test //src/test/java/com/williamfiset/algorithms/dp:MaximumSubarrayTest
java_test(
name = "MaximumSubarrayTest",
srcs = ["MaximumSubarrayTest.java"],
main_class = "org.junit.platform.console.ConsoleLauncher",
use_testrunner = False,
args = ["--select-class=com.williamfiset.algorithms.dp.MaximumSubarrayTest"],
runtime_deps = JUNIT5_RUNTIME_DEPS,
deps = TEST_DEPS,
)

# Run all tests
# bazel test //src/test/java/com/williamfiset/algorithms/dp:all
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
package com.williamfiset.algorithms.dp;

import static com.google.common.truth.Truth.assertThat;
import static org.junit.jupiter.api.Assertions.assertThrows;

import org.junit.jupiter.api.Test;

public class MaximumSubarrayTest {

@Test
public void testNullInput() {
assertThrows(
IllegalArgumentException.class, () -> MaximumSubarray.maximumSubarrayValue(null));
}

@Test
public void testEmptyInput() {
assertThrows(
IllegalArgumentException.class, () -> MaximumSubarray.maximumSubarrayValue(new int[] {}));
}

@Test
public void testSinglePositive() {
assertThat(MaximumSubarray.maximumSubarrayValue(new int[] {7})).isEqualTo(7);
}

@Test
public void testSingleNegative() {
assertThat(MaximumSubarray.maximumSubarrayValue(new int[] {-5})).isEqualTo(-5);
}

@Test
public void testSingleZero() {
assertThat(MaximumSubarray.maximumSubarrayValue(new int[] {0})).isEqualTo(0);
}

@Test
public void testAllPositive() {
// Entire array is the max subarray
assertThat(MaximumSubarray.maximumSubarrayValue(new int[] {1, 2, 3, 4})).isEqualTo(10);
}

/** All negative: the max subarray is the single largest element. */
@Test
public void testAllNegative() {
assertThat(MaximumSubarray.maximumSubarrayValue(
new int[] {-5, -4, -10, -3, -1, -12, -6})).isEqualTo(-1);
}

@Test
public void testMixedWithNegativeReset() {
// [2, -1, 40] = 41
assertThat(MaximumSubarray.maximumSubarrayValue(
new int[] {1, 2, 1, -7, 2, -1, 40, -89})).isEqualTo(41);
}

@Test
public void testMaxSubarrayAtStart() {
// [5, 4] = 9
assertThat(MaximumSubarray.maximumSubarrayValue(new int[] {5, 4, -20, 1, 2})).isEqualTo(9);
}

@Test
public void testMaxSubarrayAtEnd() {
// [3, 7] = 10
assertThat(MaximumSubarray.maximumSubarrayValue(new int[] {1, -20, 3, 7})).isEqualTo(10);
}

@Test
public void testMaxSubarrayInMiddle() {
// [4, -1, 5] = 8
assertThat(MaximumSubarray.maximumSubarrayValue(
new int[] {-3, 4, -1, 5, -10})).isEqualTo(8);
}

@Test
public void testEntireArrayIsMax() {
assertThat(MaximumSubarray.maximumSubarrayValue(
new int[] {2, -1, 3, -1, 2})).isEqualTo(5);
}

@Test
public void testAllZeros() {
assertThat(MaximumSubarray.maximumSubarrayValue(new int[] {0, 0, 0})).isEqualTo(0);
}

@Test
public void testLargeValues() {
// Ensure long return type handles values beyond int range
assertThat(MaximumSubarray.maximumSubarrayValue(
new int[] {Integer.MAX_VALUE, Integer.MAX_VALUE}))
.isEqualTo(2L * Integer.MAX_VALUE);
}
}
Loading