From b91a393bf98d65f1ed977164ba90948a9c9c3bc8 Mon Sep 17 00:00:00 2001 From: Hardik Pawar Date: Sun, 5 Oct 2025 16:20:55 +0530 Subject: [PATCH 1/7] refactor: Enhance docs, code, add tests in `MaximumSumOfDistinctSubarraysWithLengthK` --- ...imumSumOfDistinctSubarraysWithLengthK.java | 98 +++++++----- ...SumOfDistinctSubarraysWithLengthKTest.java | 144 +++++++++++++++++- 2 files changed, 199 insertions(+), 43 deletions(-) diff --git a/src/main/java/com/thealgorithms/others/MaximumSumOfDistinctSubarraysWithLengthK.java b/src/main/java/com/thealgorithms/others/MaximumSumOfDistinctSubarraysWithLengthK.java index c05f1af4e327..dec813dd3213 100644 --- a/src/main/java/com/thealgorithms/others/MaximumSumOfDistinctSubarraysWithLengthK.java +++ b/src/main/java/com/thealgorithms/others/MaximumSumOfDistinctSubarraysWithLengthK.java @@ -1,14 +1,26 @@ package com.thealgorithms.others; -import java.util.HashSet; -import java.util.Set; +import java.util.HashMap; +import java.util.Map; /** - * References: https://en.wikipedia.org/wiki/Streaming_algorithm + * Algorithm to find the maximum sum of a subarray of size K with all distinct + * elements. * - * This model involves computing the maximum sum of subarrays of a fixed size \( K \) from a stream of integers. - * As the stream progresses, elements from the end of the window are removed, and new elements from the stream are added. + * This implementation uses a sliding window approach with a hash map to + * efficiently + * track element frequencies within the current window. The algorithm maintains + * a window + * of size K and slides it across the array, ensuring all elements in the window + * are distinct. * + * Time Complexity: O(n) where n is the length of the input array + * Space Complexity: O(k) for storing elements in the hash map + * + * @see Streaming + * Algorithm + * @see Sliding + * Window * @author Swarga-codes (https://github.com/Swarga-codes) */ public final class MaximumSumOfDistinctSubarraysWithLengthK { @@ -16,54 +28,62 @@ private MaximumSumOfDistinctSubarraysWithLengthK() { } /** - * Finds the maximum sum of a subarray of size K consisting of distinct elements. + * Finds the maximum sum of a subarray of size K consisting of distinct + * elements. * - * @param k The size of the subarray. - * @param nums The array from which subarrays will be considered. + * The algorithm uses a sliding window technique with a frequency map to track + * the count of each element in the current window. A window is valid only if + * all K elements are distinct (frequency map size equals K). * - * @return The maximum sum of any distinct-element subarray of size K. If no such subarray exists, returns 0. + * @param k The size of the subarray. Must be non-negative. + * @param nums The array from which subarrays will be considered. + * @return The maximum sum of any distinct-element subarray of size K. + * Returns 0 if no such subarray exists or if k is 0 or negative. + * @throws IllegalArgumentException if k is negative */ public static long maximumSubarraySum(int k, int... nums) { - if (nums.length < k) { + if (k <= 0 || nums == null || nums.length < k) { return 0; } - long masSum = 0; // Variable to store the maximum sum of distinct subarrays - long currentSum = 0; // Variable to store the sum of the current subarray - Set currentSet = new HashSet<>(); // Set to track distinct elements in the current subarray - // Initialize the first window + long maxSum = 0; + long currentSum = 0; + Map frequencyMap = new HashMap<>(); + + // Initialize the first window of size k for (int i = 0; i < k; i++) { currentSum += nums[i]; - currentSet.add(nums[i]); + frequencyMap.put(nums[i], frequencyMap.getOrDefault(nums[i], 0) + 1); } - // If the first window contains distinct elements, update maxSum - if (currentSet.size() == k) { - masSum = currentSum; + + // Check if the first window has all distinct elements + if (frequencyMap.size() == k) { + maxSum = currentSum; } + // Slide the window across the array - for (int i = 1; i < nums.length - k + 1; i++) { - // Update the sum by removing the element that is sliding out and adding the new element - currentSum = currentSum - nums[i - 1]; - currentSum = currentSum + nums[i + k - 1]; - int j = i; - boolean flag = false; // flag value which says that the subarray contains distinct elements - while (j < i + k && currentSet.size() < k) { - if (nums[i - 1] == nums[j]) { - flag = true; - break; - } else { - j++; - } - } - if (!flag) { - currentSet.remove(nums[i - 1]); + for (int i = k; i < nums.length; i++) { + // Remove the leftmost element from the window + int leftElement = nums[i - k]; + currentSum -= leftElement; + int leftFrequency = frequencyMap.get(leftElement); + if (leftFrequency == 1) { + frequencyMap.remove(leftElement); + } else { + frequencyMap.put(leftElement, leftFrequency - 1); } - currentSet.add(nums[i + k - 1]); - // If the current window has distinct elements, compare and possibly update maxSum - if (currentSet.size() == k && masSum < currentSum) { - masSum = currentSum; + + // Add the new rightmost element to the window + int rightElement = nums[i]; + currentSum += rightElement; + frequencyMap.put(rightElement, frequencyMap.getOrDefault(rightElement, 0) + 1); + + // If all elements in the window are distinct, update maxSum if needed + if (frequencyMap.size() == k && currentSum > maxSum) { + maxSum = currentSum; } } - return masSum; // the final maximum sum + + return maxSum; } } diff --git a/src/test/java/com/thealgorithms/others/MaximumSumOfDistinctSubarraysWithLengthKTest.java b/src/test/java/com/thealgorithms/others/MaximumSumOfDistinctSubarraysWithLengthKTest.java index f360e3f53546..f5353fba4f7f 100644 --- a/src/test/java/com/thealgorithms/others/MaximumSumOfDistinctSubarraysWithLengthKTest.java +++ b/src/test/java/com/thealgorithms/others/MaximumSumOfDistinctSubarraysWithLengthKTest.java @@ -3,20 +3,156 @@ import static org.junit.jupiter.api.Assertions.assertEquals; import java.util.stream.Stream; +import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.Arguments; import org.junit.jupiter.params.provider.MethodSource; -public class MaximumSumOfDistinctSubarraysWithLengthKTest { +/** + * Test class for {@link MaximumSumOfDistinctSubarraysWithLengthK}. + * + * This class contains comprehensive test cases to verify the correctness of the + * maximum subarray sum algorithm with distinct elements constraint. + */ +class MaximumSumOfDistinctSubarraysWithLengthKTest { + /** + * Parameterized test for various input scenarios. + * + * @param expected the expected maximum sum + * @param k the subarray size + * @param arr the input array + */ @ParameterizedTest @MethodSource("inputStream") - void testMaximumSubarraySum(int expected, int k, int[] arr) { + void testMaximumSubarraySum(long expected, int k, int[] arr) { assertEquals(expected, MaximumSumOfDistinctSubarraysWithLengthK.maximumSubarraySum(k, arr)); } + /** + * Provides test cases for the parameterized test. + * + * Test cases cover: + * - Normal cases with distinct and duplicate elements + * - Edge cases (empty array, k = 0, k > array length) + * - Single element arrays + * - Arrays with all duplicates + * - Negative numbers + * - Large sums + * + * @return stream of test arguments + */ private static Stream inputStream() { - return Stream.of(Arguments.of(15, 3, new int[] {1, 5, 4, 2, 9, 9, 9}), Arguments.of(0, 3, new int[] {4, 4, 4}), Arguments.of(12, 3, new int[] {9, 9, 9, 1, 2, 3}), Arguments.of(0, 0, new int[] {9, 9, 9}), Arguments.of(0, 5, new int[] {9, 9, 9}), Arguments.of(9, 1, new int[] {9, 2, 3, 7}), - Arguments.of(15, 5, new int[] {1, 2, 3, 4, 5}), Arguments.of(6, 3, new int[] {-1, 2, 3, 1, -2, 4}), Arguments.of(10, 1, new int[] {10}), Arguments.of(0, 2, new int[] {7, 7, 7, 7}), Arguments.of(0, 3, new int[] {}), Arguments.of(0, 10, new int[] {1, 2, 3})); + return Stream.of( + // Normal case: [5, 4, 2] has distinct elements with sum 11, but [4, 2, 9] also + // distinct with sum 15 + Arguments.of(15L, 3, new int[] {1, 5, 4, 2, 9, 9, 9}), + // All elements are same, no distinct subarray of size 3 + Arguments.of(0L, 3, new int[] {4, 4, 4}), + // First three have duplicates, but [1, 2, 3] are distinct with sum 6, wait + // [9,1,2] has sum 12 + Arguments.of(12L, 3, new int[] {9, 9, 9, 1, 2, 3}), + // k = 0, should return 0 + Arguments.of(0L, 0, new int[] {9, 9, 9}), + // k > array length, should return 0 + Arguments.of(0L, 5, new int[] {9, 9, 9}), + // k = 1, single element (always distinct) + Arguments.of(9L, 1, new int[] {9, 2, 3, 7}), + // All distinct elements, size matches array + Arguments.of(15L, 5, new int[] {1, 2, 3, 4, 5}), + // Array with negative numbers + Arguments.of(6L, 3, new int[] {-1, 2, 3, 1, -2, 4}), + // Single element array + Arguments.of(10L, 1, new int[] {10}), + // All duplicates with k = 2 + Arguments.of(0L, 2, new int[] {7, 7, 7, 7}), + // Empty array + Arguments.of(0L, 3, new int[] {}), + // k much larger than array length + Arguments.of(0L, 10, new int[] {1, 2, 3})); + } + + /** + * Test with a larger array and larger k value. + */ + @Test + void testLargerArray() { + int[] arr = new int[] {1, 2, 3, 4, 5, 6, 7, 8, 9, 10}; + long result = MaximumSumOfDistinctSubarraysWithLengthK.maximumSubarraySum(5, arr); + // Maximum sum with 5 distinct elements: [6,7,8,9,10] = 40 + assertEquals(40L, result); + } + + /** + * Test with negative k value. + */ + @Test + void testNegativeK() { + int[] arr = new int[] {1, 2, 3, 4, 5}; + long result = MaximumSumOfDistinctSubarraysWithLengthK.maximumSubarraySum(-1, arr); + assertEquals(0L, result); + } + + /** + * Test with null array. + */ + @Test + void testNullArray() { + long result = MaximumSumOfDistinctSubarraysWithLengthK.maximumSubarraySum(3, (int[]) null); + assertEquals(0L, result); + } + + /** + * Test with array containing duplicates at boundaries. + */ + @Test + void testDuplicatesAtBoundaries() { + int[] arr = new int[] {1, 1, 2, 3, 4, 4}; + // [2, 3, 4] is the only valid window with sum 9 + long result = MaximumSumOfDistinctSubarraysWithLengthK.maximumSubarraySum(3, arr); + assertEquals(9L, result); + } + + /** + * Test with large numbers to verify long return type. + */ + @Test + void testLargeNumbers() { + int[] arr = new int[] {1000000, 2000000, 3000000, 4000000}; + // All elements are distinct, max sum with k=3 is [2000000, 3000000, 4000000] = + // 9000000 + long result = MaximumSumOfDistinctSubarraysWithLengthK.maximumSubarraySum(3, arr); + assertEquals(9000000L, result); + } + + /** + * Test where multiple windows have the same maximum sum. + */ + @Test + void testMultipleMaxWindows() { + int[] arr = new int[] {1, 2, 3, 4, 3, 2, 1}; + // Windows [1,2,3], [2,3,4], [4,3,2], [3,2,1] - max is [2,3,4] = 9 + long result = MaximumSumOfDistinctSubarraysWithLengthK.maximumSubarraySum(3, arr); + assertEquals(9L, result); + } + + /** + * Test with only two elements and k=2. + */ + @Test + void testTwoElementsDistinct() { + int[] arr = new int[] {5, 10}; + long result = MaximumSumOfDistinctSubarraysWithLengthK.maximumSubarraySum(2, arr); + assertEquals(15L, result); + } + + /** + * Test with only two elements (duplicates) and k=2. + */ + @Test + void testTwoElementsDuplicate() { + int[] arr = new int[] {5, 5}; + long result = MaximumSumOfDistinctSubarraysWithLengthK.maximumSubarraySum(2, arr); + assertEquals(0L, result); } } From f06858812aa47cce133ed10387973ef5350c03c0 Mon Sep 17 00:00:00 2001 From: Hardik Pawar Date: Sun, 5 Oct 2025 16:22:03 +0530 Subject: [PATCH 2/7] Fix --- .../others/MaximumSumOfDistinctSubarraysWithLengthKTest.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/test/java/com/thealgorithms/others/MaximumSumOfDistinctSubarraysWithLengthKTest.java b/src/test/java/com/thealgorithms/others/MaximumSumOfDistinctSubarraysWithLengthKTest.java index f5353fba4f7f..549674b91e79 100644 --- a/src/test/java/com/thealgorithms/others/MaximumSumOfDistinctSubarraysWithLengthKTest.java +++ b/src/test/java/com/thealgorithms/others/MaximumSumOfDistinctSubarraysWithLengthKTest.java @@ -98,7 +98,8 @@ void testNegativeK() { */ @Test void testNullArray() { - long result = MaximumSumOfDistinctSubarraysWithLengthK.maximumSubarraySum(3, (int[]) null); + int[] arr = null; + long result = MaximumSumOfDistinctSubarraysWithLengthK.maximumSubarraySum(3, arr); assertEquals(0L, result); } From 3fce1342ccf09aac772c9b6fa6443fb0ed0a72de Mon Sep 17 00:00:00 2001 From: Hardik Pawar Date: Sun, 12 Oct 2025 11:17:51 +0530 Subject: [PATCH 3/7] Fix spotbug --- .../others/MaximumSumOfDistinctSubarraysWithLengthKTest.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/test/java/com/thealgorithms/others/MaximumSumOfDistinctSubarraysWithLengthKTest.java b/src/test/java/com/thealgorithms/others/MaximumSumOfDistinctSubarraysWithLengthKTest.java index 549674b91e79..f5353fba4f7f 100644 --- a/src/test/java/com/thealgorithms/others/MaximumSumOfDistinctSubarraysWithLengthKTest.java +++ b/src/test/java/com/thealgorithms/others/MaximumSumOfDistinctSubarraysWithLengthKTest.java @@ -98,8 +98,7 @@ void testNegativeK() { */ @Test void testNullArray() { - int[] arr = null; - long result = MaximumSumOfDistinctSubarraysWithLengthK.maximumSubarraySum(3, arr); + long result = MaximumSumOfDistinctSubarraysWithLengthK.maximumSubarraySum(3, (int[]) null); assertEquals(0L, result); } From cf2c02a38b4827396898924db5da9eda5ab829c3 Mon Sep 17 00:00:00 2001 From: Hardik Pawar Date: Sun, 12 Oct 2025 11:34:58 +0530 Subject: [PATCH 4/7] Fix --- .../others/MaximumSumOfDistinctSubarraysWithLengthKTest.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/test/java/com/thealgorithms/others/MaximumSumOfDistinctSubarraysWithLengthKTest.java b/src/test/java/com/thealgorithms/others/MaximumSumOfDistinctSubarraysWithLengthKTest.java index f5353fba4f7f..70ede7b0d0a7 100644 --- a/src/test/java/com/thealgorithms/others/MaximumSumOfDistinctSubarraysWithLengthKTest.java +++ b/src/test/java/com/thealgorithms/others/MaximumSumOfDistinctSubarraysWithLengthKTest.java @@ -98,7 +98,8 @@ void testNegativeK() { */ @Test void testNullArray() { - long result = MaximumSumOfDistinctSubarraysWithLengthK.maximumSubarraySum(3, (int[]) null); + int[] nullArray = null; + long result = MaximumSumOfDistinctSubarraysWithLengthK.maximumSubarraySum(3, nullArray); assertEquals(0L, result); } From 6d2efa491811133bba90198d83685641b4d19fb7 Mon Sep 17 00:00:00 2001 From: Hardik Pawar Date: Sun, 12 Oct 2025 11:39:47 +0530 Subject: [PATCH 5/7] Fix --- .../others/MaximumSumOfDistinctSubarraysWithLengthKTest.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/test/java/com/thealgorithms/others/MaximumSumOfDistinctSubarraysWithLengthKTest.java b/src/test/java/com/thealgorithms/others/MaximumSumOfDistinctSubarraysWithLengthKTest.java index 70ede7b0d0a7..f5353fba4f7f 100644 --- a/src/test/java/com/thealgorithms/others/MaximumSumOfDistinctSubarraysWithLengthKTest.java +++ b/src/test/java/com/thealgorithms/others/MaximumSumOfDistinctSubarraysWithLengthKTest.java @@ -98,8 +98,7 @@ void testNegativeK() { */ @Test void testNullArray() { - int[] nullArray = null; - long result = MaximumSumOfDistinctSubarraysWithLengthK.maximumSubarraySum(3, nullArray); + long result = MaximumSumOfDistinctSubarraysWithLengthK.maximumSubarraySum(3, (int[]) null); assertEquals(0L, result); } From c3a653e2d5fb9277c3e3f74b2376e2e9c40daa9d Mon Sep 17 00:00:00 2001 From: Hardik Pawar Date: Sun, 12 Oct 2025 11:44:38 +0530 Subject: [PATCH 6/7] Fix --- .../others/MaximumSumOfDistinctSubarraysWithLengthKTest.java | 1 + 1 file changed, 1 insertion(+) diff --git a/src/test/java/com/thealgorithms/others/MaximumSumOfDistinctSubarraysWithLengthKTest.java b/src/test/java/com/thealgorithms/others/MaximumSumOfDistinctSubarraysWithLengthKTest.java index f5353fba4f7f..c97fa93add0e 100644 --- a/src/test/java/com/thealgorithms/others/MaximumSumOfDistinctSubarraysWithLengthKTest.java +++ b/src/test/java/com/thealgorithms/others/MaximumSumOfDistinctSubarraysWithLengthKTest.java @@ -97,6 +97,7 @@ void testNegativeK() { * Test with null array. */ @Test + @SuppressWarnings("NullAway") void testNullArray() { long result = MaximumSumOfDistinctSubarraysWithLengthK.maximumSubarraySum(3, (int[]) null); assertEquals(0L, result); From 85c856f117a17a65e1881ab5d362ea5811b7f29f Mon Sep 17 00:00:00 2001 From: Hardik Pawar Date: Sun, 12 Oct 2025 11:46:58 +0530 Subject: [PATCH 7/7] Fix --- .../others/MaximumSumOfDistinctSubarraysWithLengthKTest.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/test/java/com/thealgorithms/others/MaximumSumOfDistinctSubarraysWithLengthKTest.java b/src/test/java/com/thealgorithms/others/MaximumSumOfDistinctSubarraysWithLengthKTest.java index c97fa93add0e..1a42f1815a96 100644 --- a/src/test/java/com/thealgorithms/others/MaximumSumOfDistinctSubarraysWithLengthKTest.java +++ b/src/test/java/com/thealgorithms/others/MaximumSumOfDistinctSubarraysWithLengthKTest.java @@ -97,9 +97,9 @@ void testNegativeK() { * Test with null array. */ @Test - @SuppressWarnings("NullAway") void testNullArray() { - long result = MaximumSumOfDistinctSubarraysWithLengthK.maximumSubarraySum(3, (int[]) null); + int[] nullArray = null; + long result = MaximumSumOfDistinctSubarraysWithLengthK.maximumSubarraySum(3, new int[][] {nullArray}[0]); assertEquals(0L, result); }