From 68fef34a3a86f9346e0799d56853927a05216e01 Mon Sep 17 00:00:00 2001 From: orthodox-64 <151670745+Orthodox-64@users.noreply.github.com> Date: Thu, 9 Oct 2025 01:48:46 +0530 Subject: [PATCH 1/6] feat-Add MaximumProductSubarray --- .../MaximumProductSubarray.java | 105 +++++++++++++ .../MaximumProductSubarrayTest.java | 140 ++++++++++++++++++ 2 files changed, 245 insertions(+) create mode 100644 src/main/java/com/thealgorithms/dynamicprogramming/MaximumProductSubarray.java create mode 100644 src/test/java/com/thealgorithms/dynamicprogramming/MaximumProductSubarrayTest.java diff --git a/src/main/java/com/thealgorithms/dynamicprogramming/MaximumProductSubarray.java b/src/main/java/com/thealgorithms/dynamicprogramming/MaximumProductSubarray.java new file mode 100644 index 000000000000..d100a91e4325 --- /dev/null +++ b/src/main/java/com/thealgorithms/dynamicprogramming/MaximumProductSubarray.java @@ -0,0 +1,105 @@ +package com.thealgorithms.dynamicprogramming; + +/** + * The MaximumProductSubarray class implements the algorithm to find the + * maximum product of a contiguous subarray within a given array of integers. + * + *

Given an array of integers (which may contain positive numbers, negative + * numbers, and zeros), this algorithm finds the contiguous subarray that has + * the largest product. The algorithm handles negative numbers efficiently by + * tracking both maximum and minimum products, since a negative number can turn + * a minimum product into a maximum product.

+ * + *

This implementation uses a dynamic programming approach that runs in O(n) + * time complexity and O(1) space complexity, making it highly efficient for + * large arrays.

+ */ +public final class MaximumProductSubarray { + private MaximumProductSubarray() { + } + + /** + * Finds the maximum product of any contiguous subarray in the given array. + * + * @param nums an array of integers which may contain positive, negative, + * and zero values. + * @return the maximum product of a contiguous subarray. Returns 0 if the + * array is empty. + */ + public static int maxProduct(int[] nums) { + if (nums == null || nums.length == 0) { + return 0; + } + + int maxProduct = nums[0]; + int currentMax = nums[0]; + int currentMin = nums[0]; + + for (int i = 1; i < nums.length; i++) { + if (nums[i] < 0) { + int temp = currentMax; + currentMax = currentMin; + currentMin = temp; + } + + currentMax = Math.max(nums[i], currentMax * nums[i]); + currentMin = Math.min(nums[i], currentMin * nums[i]); + + maxProduct = Math.max(maxProduct, currentMax); + } + + return maxProduct; + } + + /** + * Finds the maximum product using a memoization approach with recursion. + * This method explores all possible subarrays and stores intermediate results. + * + * @param nums an array of integers which may contain positive, negative, + * and zero values. + * @return the maximum product of a contiguous subarray. Returns 0 if the + * array is empty. + */ + public static int maxProductMemoized(int[] nums) { + if (nums == null || nums.length == 0) { + return 0; + } + + int n = nums.length; + Integer[][] memo = new Integer[n][n]; + int maxProduct = Integer.MIN_VALUE; + + for (int i = 0; i < n; i++) { + for (int j = i; j < n; j++) { + maxProduct = Math.max(maxProduct, calculateProduct(nums, memo, i, j)); + } + } + + return maxProduct; + } + + /** + * A recursive helper method to calculate the product of elements from index + * start to index end using memoization. + * + * @param nums the input array of integers. + * @param memo the memoization table storing the results of subproblems. + * @param start the starting index of the subarray. + * @param end the ending index of the subarray. + * @return the product of elements from start to end. + */ + private static int calculateProduct(int[] nums, Integer[][] memo, int start, int end) { + if (memo[start][end] != null) { + return memo[start][end]; + } + + if (start == end) { + memo[start][end] = nums[start]; + return nums[start]; + } + + int product = calculateProduct(nums, memo, start, end - 1) * nums[end]; + memo[start][end] = product; + return product; + } +} \ No newline at end of file diff --git a/src/test/java/com/thealgorithms/dynamicprogramming/MaximumProductSubarrayTest.java b/src/test/java/com/thealgorithms/dynamicprogramming/MaximumProductSubarrayTest.java new file mode 100644 index 000000000000..46d787ef86a7 --- /dev/null +++ b/src/test/java/com/thealgorithms/dynamicprogramming/MaximumProductSubarrayTest.java @@ -0,0 +1,140 @@ +package com.thealgorithms.dynamicprogramming; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import org.junit.jupiter.api.Test; + +class MaximumProductSubarrayTest { + + /** + * Test case for an array with all positive numbers. + * The expected maximum product is the product of all elements. + */ + @Test + void testAllPositiveNumbers() { + int[] nums = {2, 3, 4}; + int expected = 24; + int actual = MaximumProductSubarray.maxProduct(nums); + assertEquals(expected, actual, "The maximum product should be 24."); + } + + /** + * Test case for an array with positive and negative numbers. + * The expected maximum product is 24 (subarray [2, -3, -4]). + */ + @Test + void testMixedPositiveAndNegative() { + int[] nums = {2, -3, -4, 1}; + int expected = 24; + int actual = MaximumProductSubarray.maxProduct(nums); + assertEquals(expected, actual, "The maximum product should be 24."); + } + + /** + * Test case for an array containing zeros. + * The expected maximum product is 24 (subarray [2, 3, 4]). + */ + @Test + void testArrayWithZeros() { + int[] nums = {2, 3, 0, 4, 6}; + int expected = 24; + int actual = MaximumProductSubarray.maxProduct(nums); + assertEquals(expected, actual, "The maximum product should be 24."); + } + + /** + * Test case for an array with a single element. + * The expected maximum product is the element itself. + */ + @Test + void testSingleElement() { + int[] nums = {5}; + int expected = 5; + int actual = MaximumProductSubarray.maxProduct(nums); + assertEquals(expected, actual, "The maximum product should be 5."); + } + + /** + * Test case for an array with all negative numbers. + * The expected maximum product is the largest single negative number. + */ + @Test + void testAllNegativeNumbers() { + int[] nums = {-2, -3, -4}; + int expected = 12; + int actual = MaximumProductSubarray.maxProduct(nums); + assertEquals(expected, actual, "The maximum product should be 12."); + } + + /** + * Test case for an array with negative numbers where odd count of negatives + * breaks the chain. The expected maximum product is 60 (subarray [-2, -3, 10]). + */ + @Test + void testOddNegativeNumbers() { + int[] nums = {-2, -3, 10, -1}; + int expected = 60; + int actual = MaximumProductSubarray.maxProduct(nums); + assertEquals(expected, actual, "The maximum product should be 60."); + } + + /** + * Test case for an empty array. + * The expected maximum product is 0. + */ + @Test + void testEmptyArray() { + int[] nums = {}; + int expected = 0; + int actual = MaximumProductSubarray.maxProduct(nums); + assertEquals(expected, actual, "The maximum product should be 0 for an empty array."); + } + + /** + * Test case for an array with alternating positive and negative numbers. + * The expected maximum product is 6 (subarray [2, 3]). + */ + @Test + void testAlternatingNumbers() { + int[] nums = {2, 3, -2, 4}; + int expected = 6; + int actual = MaximumProductSubarray.maxProduct(nums); + assertEquals(expected, actual, "The maximum product should be 6."); + } + + /** + * Test case for the memoized version with all positive numbers. + * The expected maximum product is 24. + */ + @Test + void testMemoizedAllPositiveNumbers() { + int[] nums = {2, 3, 4}; + int expected = 24; + int actual = MaximumProductSubarray.maxProductMemoized(nums); + assertEquals(expected, actual, "The maximum product should be 24."); + } + + /** + * Test case for the memoized version with mixed positive and negative numbers. + * The expected maximum product is 24. + */ + @Test + void testMemoizedMixedNumbers() { + int[] nums = {2, -3, -4, 1}; + int expected = 24; + int actual = MaximumProductSubarray.maxProductMemoized(nums); + assertEquals(expected, actual, "The maximum product should be 24."); + } + + /** + * Test case for an array with large positive and negative numbers. + * The expected maximum product is 720 (subarray [6, -3, -20]). + */ + @Test + void testLargeNumbers() { + int[] nums = {6, -3, -20, 0, 5}; + int expected = 360; + int actual = MaximumProductSubarray.maxProduct(nums); + assertEquals(expected, actual, "The maximum product should be 360."); + } +} \ No newline at end of file From ec2ac89463f2b0c6534d67fde4c3634500b791bf Mon Sep 17 00:00:00 2001 From: orthodox-64 <151670745+Orthodox-64@users.noreply.github.com> Date: Thu, 9 Oct 2025 01:58:11 +0530 Subject: [PATCH 2/6] feat-changes --- .../MaximumProductSubarray.java | 1 + .../MaximumProductSubarrayTest.java | 56 +++++++++++-------- 2 files changed, 35 insertions(+), 22 deletions(-) diff --git a/src/main/java/com/thealgorithms/dynamicprogramming/MaximumProductSubarray.java b/src/main/java/com/thealgorithms/dynamicprogramming/MaximumProductSubarray.java index d100a91e4325..a6814ff71767 100644 --- a/src/main/java/com/thealgorithms/dynamicprogramming/MaximumProductSubarray.java +++ b/src/main/java/com/thealgorithms/dynamicprogramming/MaximumProductSubarray.java @@ -50,6 +50,7 @@ public static int maxProduct(int[] nums) { return maxProduct; } +} /** * Finds the maximum product using a memoization approach with recursion. diff --git a/src/test/java/com/thealgorithms/dynamicprogramming/MaximumProductSubarrayTest.java b/src/test/java/com/thealgorithms/dynamicprogramming/MaximumProductSubarrayTest.java index 46d787ef86a7..289dec75b688 100644 --- a/src/test/java/com/thealgorithms/dynamicprogramming/MaximumProductSubarrayTest.java +++ b/src/test/java/com/thealgorithms/dynamicprogramming/MaximumProductSubarrayTest.java @@ -32,7 +32,7 @@ void testMixedPositiveAndNegative() { /** * Test case for an array containing zeros. - * The expected maximum product is 24 (subarray [2, 3, 4]). + * The expected maximum product is 24 (subarray [4, 6]). */ @Test void testArrayWithZeros() { @@ -56,7 +56,7 @@ void testSingleElement() { /** * Test case for an array with all negative numbers. - * The expected maximum product is the largest single negative number. + * The expected maximum product is 12 (subarray [-3, -4]). */ @Test void testAllNegativeNumbers() { @@ -90,6 +90,18 @@ void testEmptyArray() { assertEquals(expected, actual, "The maximum product should be 0 for an empty array."); } + /** + * Test case for a null array. + * The expected maximum product is 0. + */ + @Test + void testNullArray() { + int[] nums = null; + int expected = 0; + int actual = MaximumProductSubarray.maxProduct(nums); + assertEquals(expected, actual, "The maximum product should be 0 for a null array."); + } + /** * Test case for an array with alternating positive and negative numbers. * The expected maximum product is 6 (subarray [2, 3]). @@ -103,38 +115,38 @@ void testAlternatingNumbers() { } /** - * Test case for the memoized version with all positive numbers. - * The expected maximum product is 24. + * Test case for an array with large positive and negative numbers. + * The expected maximum product is 360 (subarray [6, -3, -20]). */ @Test - void testMemoizedAllPositiveNumbers() { - int[] nums = {2, 3, 4}; - int expected = 24; - int actual = MaximumProductSubarray.maxProductMemoized(nums); - assertEquals(expected, actual, "The maximum product should be 24."); + void testLargeNumbers() { + int[] nums = {6, -3, -20, 0, 5}; + int expected = 360; + int actual = MaximumProductSubarray.maxProduct(nums); + assertEquals(expected, actual, "The maximum product should be 360."); } /** - * Test case for the memoized version with mixed positive and negative numbers. - * The expected maximum product is 24. + * Test case for an array with single negative number. + * The expected maximum product is the negative number itself. */ @Test - void testMemoizedMixedNumbers() { - int[] nums = {2, -3, -4, 1}; - int expected = 24; - int actual = MaximumProductSubarray.maxProductMemoized(nums); - assertEquals(expected, actual, "The maximum product should be 24."); + void testSingleNegativeElement() { + int[] nums = {-8}; + int expected = -8; + int actual = MaximumProductSubarray.maxProduct(nums); + assertEquals(expected, actual, "The maximum product should be -8."); } /** - * Test case for an array with large positive and negative numbers. - * The expected maximum product is 720 (subarray [6, -3, -20]). + * Test case for an array with multiple zeros. + * The expected maximum product is 6 (subarray [2, 3]). */ @Test - void testLargeNumbers() { - int[] nums = {6, -3, -20, 0, 5}; - int expected = 360; + void testMultipleZeros() { + int[] nums = {0, 2, 3, 0, 4}; + int expected = 6; int actual = MaximumProductSubarray.maxProduct(nums); - assertEquals(expected, actual, "The maximum product should be 360."); + assertEquals(expected, actual, "The maximum product should be 6."); } } \ No newline at end of file From 6702fd03fa1129fbbba6b6d22b07771e6a26d537 Mon Sep 17 00:00:00 2001 From: orthodox-64 <151670745+Orthodox-64@users.noreply.github.com> Date: Thu, 9 Oct 2025 02:01:45 +0530 Subject: [PATCH 3/6] fix: correct MaximumProductSubarray compilation errors --- .../MaximumProductSubarray.java | 27 ------------------- 1 file changed, 27 deletions(-) diff --git a/src/main/java/com/thealgorithms/dynamicprogramming/MaximumProductSubarray.java b/src/main/java/com/thealgorithms/dynamicprogramming/MaximumProductSubarray.java index a6814ff71767..c726da08e06c 100644 --- a/src/main/java/com/thealgorithms/dynamicprogramming/MaximumProductSubarray.java +++ b/src/main/java/com/thealgorithms/dynamicprogramming/MaximumProductSubarray.java @@ -52,33 +52,6 @@ public static int maxProduct(int[] nums) { } } - /** - * Finds the maximum product using a memoization approach with recursion. - * This method explores all possible subarrays and stores intermediate results. - * - * @param nums an array of integers which may contain positive, negative, - * and zero values. - * @return the maximum product of a contiguous subarray. Returns 0 if the - * array is empty. - */ - public static int maxProductMemoized(int[] nums) { - if (nums == null || nums.length == 0) { - return 0; - } - - int n = nums.length; - Integer[][] memo = new Integer[n][n]; - int maxProduct = Integer.MIN_VALUE; - - for (int i = 0; i < n; i++) { - for (int j = i; j < n; j++) { - maxProduct = Math.max(maxProduct, calculateProduct(nums, memo, i, j)); - } - } - - return maxProduct; - } - /** * A recursive helper method to calculate the product of elements from index * start to index end using memoization. From cf7f973c605e9b174ab913551e2d25453428cebd Mon Sep 17 00:00:00 2001 From: orthodox-64 <151670745+Orthodox-64@users.noreply.github.com> Date: Thu, 9 Oct 2025 02:07:07 +0530 Subject: [PATCH 4/6] fix-lints --- .../MaximumProductSubarray.java | 33 ++++--------------- 1 file changed, 6 insertions(+), 27 deletions(-) diff --git a/src/main/java/com/thealgorithms/dynamicprogramming/MaximumProductSubarray.java b/src/main/java/com/thealgorithms/dynamicprogramming/MaximumProductSubarray.java index c726da08e06c..c2688a1dd5d2 100644 --- a/src/main/java/com/thealgorithms/dynamicprogramming/MaximumProductSubarray.java +++ b/src/main/java/com/thealgorithms/dynamicprogramming/MaximumProductSubarray.java @@ -15,7 +15,9 @@ * large arrays.

*/ public final class MaximumProductSubarray { + private MaximumProductSubarray() { + // Prevent instantiation } /** @@ -24,7 +26,7 @@ private MaximumProductSubarray() { * @param nums an array of integers which may contain positive, negative, * and zero values. * @return the maximum product of a contiguous subarray. Returns 0 if the - * array is empty. + * array is null or empty. */ public static int maxProduct(int[] nums) { if (nums == null || nums.length == 0) { @@ -36,44 +38,21 @@ public static int maxProduct(int[] nums) { int currentMin = nums[0]; for (int i = 1; i < nums.length; i++) { + // Swap currentMax and currentMin if current number is negative if (nums[i] < 0) { int temp = currentMax; currentMax = currentMin; currentMin = temp; } + // Update currentMax and currentMin currentMax = Math.max(nums[i], currentMax * nums[i]); currentMin = Math.min(nums[i], currentMin * nums[i]); + // Update global max product maxProduct = Math.max(maxProduct, currentMax); } return maxProduct; } } - - /** - * A recursive helper method to calculate the product of elements from index - * start to index end using memoization. - * - * @param nums the input array of integers. - * @param memo the memoization table storing the results of subproblems. - * @param start the starting index of the subarray. - * @param end the ending index of the subarray. - * @return the product of elements from start to end. - */ - private static int calculateProduct(int[] nums, Integer[][] memo, int start, int end) { - if (memo[start][end] != null) { - return memo[start][end]; - } - - if (start == end) { - memo[start][end] = nums[start]; - return nums[start]; - } - - int product = calculateProduct(nums, memo, start, end - 1) * nums[end]; - memo[start][end] = product; - return product; - } -} \ No newline at end of file From dfa04d6d4627f056556543a0509d6da9832c48bc Mon Sep 17 00:00:00 2001 From: orthodox-64 <151670745+Orthodox-64@users.noreply.github.com> Date: Thu, 9 Oct 2025 02:16:41 +0530 Subject: [PATCH 5/6] fix: apply clang-format to MaximumProductSubarrayTest.java --- .../dynamicprogramming/MaximumProductSubarrayTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/test/java/com/thealgorithms/dynamicprogramming/MaximumProductSubarrayTest.java b/src/test/java/com/thealgorithms/dynamicprogramming/MaximumProductSubarrayTest.java index 289dec75b688..0f499bf2a2f7 100644 --- a/src/test/java/com/thealgorithms/dynamicprogramming/MaximumProductSubarrayTest.java +++ b/src/test/java/com/thealgorithms/dynamicprogramming/MaximumProductSubarrayTest.java @@ -149,4 +149,4 @@ void testMultipleZeros() { int actual = MaximumProductSubarray.maxProduct(nums); assertEquals(expected, actual, "The maximum product should be 6."); } -} \ No newline at end of file +} From 34d2e4e1fdeeb7640b6f66c5394b45da33d1970d Mon Sep 17 00:00:00 2001 From: orthodox-64 <151670745+Orthodox-64@users.noreply.github.com> Date: Thu, 9 Oct 2025 02:22:03 +0530 Subject: [PATCH 6/6] fix: prevent integer overflow in MaximumProductSubarray using long type --- .../dynamicprogramming/MaximumProductSubarray.java | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/main/java/com/thealgorithms/dynamicprogramming/MaximumProductSubarray.java b/src/main/java/com/thealgorithms/dynamicprogramming/MaximumProductSubarray.java index c2688a1dd5d2..1c16f612e98f 100644 --- a/src/main/java/com/thealgorithms/dynamicprogramming/MaximumProductSubarray.java +++ b/src/main/java/com/thealgorithms/dynamicprogramming/MaximumProductSubarray.java @@ -33,14 +33,14 @@ public static int maxProduct(int[] nums) { return 0; } - int maxProduct = nums[0]; - int currentMax = nums[0]; - int currentMin = nums[0]; + long maxProduct = nums[0]; + long currentMax = nums[0]; + long currentMin = nums[0]; for (int i = 1; i < nums.length; i++) { // Swap currentMax and currentMin if current number is negative if (nums[i] < 0) { - int temp = currentMax; + long temp = currentMax; currentMax = currentMin; currentMin = temp; } @@ -53,6 +53,6 @@ public static int maxProduct(int[] nums) { maxProduct = Math.max(maxProduct, currentMax); } - return maxProduct; + return (int) maxProduct; } }