diff --git a/src/main/java/com/thealgorithms/slidingwindow/MaxFrequencyAfterOperations.java b/src/main/java/com/thealgorithms/slidingwindow/MaxFrequencyAfterOperations.java new file mode 100644 index 000000000000..374f7cad76dd --- /dev/null +++ b/src/main/java/com/thealgorithms/slidingwindow/MaxFrequencyAfterOperations.java @@ -0,0 +1,123 @@ +package com.thealgorithms.slidingwindow; + +import java.util.Arrays; + +/** + * Implementation of the algorithm to compute the maximum possible frequency of + * any element + * in an integer array after performing a fixed number of operations. + * + *

+ * In each operation, you can: + *

+ * + *

+ * The goal is to maximize the frequency of the most common element after all + * operations. + *

+ * + *

+ * Example: + *

+ * + *
+ * Input: nums = [1,4,5], k = 1, numOperations = 2
+ * Output: 2
+ * Explanation:
+ * - Add 0 to nums[1] → [1,4,5]
+ * - Add -1 to nums[2] → [1,4,4]
+ * The maximum frequency is 2.
+ * 
+ * + *

+ * Time Complexity: O(n log n) + *
+ * Because the array is sorted initially using {@link Arrays#sort}, which takes + * O(n log n), + * and the sliding window traversal over the sorted array runs in O(n) time. + *

+ * + *

+ * Space Complexity: O(1) + *
+ * The algorithm modifies and scans the array in place without using any + * additional data structures proportional to the input size. + *

+ */ +public final class MaxFrequencyAfterOperations { + + private MaxFrequencyAfterOperations() { + // Utility class; prevent instantiation + } + + /** + * Computes the maximum possible frequency of any element in {@code nums} + * after performing {@code numOperations} modifications where each modification + * allows adding an integer in [-k, k] to a previously unused index. + * + * @param nums the input array of integers + * @param k the maximum absolute value that can be added or + * subtracted per operation + * @param numOperations the number of operations allowed + * @return the maximum achievable frequency after operations + * @throws NullPointerException if {@code nums} is {@code null} + * + *

+ * Time Complexity: O(n log n) + *

+ *

+ * Space Complexity: O(1) + *

+ */ + public static int maxFrequency(int[] nums, int k, int numOperations) { + if (nums == null) { + throw new NullPointerException("Input array cannot be null."); + } + + // Sort the array for efficient sliding window traversal + Arrays.sort(nums); + int n = nums.length; + + // Sliding window pointers and counters + int l1 = 0, r1 = 0, cnt1 = 0; + int l2 = 0, cnt2 = 0; + int sameCnt = 0, prev = Integer.MIN_VALUE; + int maxF = 0; + + for (int x : nums) { + if (x == prev) + sameCnt++; + else { + prev = x; + sameCnt = 1; + } + + // Window for elements within [x - k, x] + while (nums[l1] < x - k) { + cnt1--; + l1++; + } + while (r1 < n && nums[r1] <= x + k) { + cnt1++; + r1++; + } + + // Option 1: Adjust within ±k + maxF = Math.max(maxF, sameCnt + Math.min(cnt1 - sameCnt, numOperations)); + + // Option 2: Adjust within ±2k + cnt2++; + while (nums[l2] < x - 2L * k) { + cnt2--; + l2++; + } + + maxF = Math.max(maxF, Math.min(cnt2, numOperations)); + } + + return maxF; + } +} diff --git a/src/test/java/com/thealgorithms/slidingwindow/MaxFrequencyAfterOperationsTest.java b/src/test/java/com/thealgorithms/slidingwindow/MaxFrequencyAfterOperationsTest.java new file mode 100644 index 000000000000..e4dcf83b2928 --- /dev/null +++ b/src/test/java/com/thealgorithms/slidingwindow/MaxFrequencyAfterOperationsTest.java @@ -0,0 +1,69 @@ +package com.thealgorithms.slidingwindow; + +import static org.junit.jupiter.api.Assertions.*; +import org.junit.jupiter.api.Test; + +/** + * Unit tests for {@link MaxFrequencyAfterOperations}. + */ +public class MaxFrequencyAfterOperationsTest { + + @Test + public void testExample1() { + int[] nums = { 1, 4, 5 }; + int k = 1, ops = 2; + int result = MaxFrequencyAfterOperations.maxFrequency(nums, k, ops); + assertEquals(2, result); + } + + @Test + public void testExample2() { + int[] nums = { 5, 11, 20, 20 }; + int k = 5, ops = 1; + int result = MaxFrequencyAfterOperations.maxFrequency(nums, k, ops); + assertEquals(2, result); + } + + @Test + public void testNoOperations() { + int[] nums = { 1, 2, 3, 4 }; + int k = 2, ops = 0; + int result = MaxFrequencyAfterOperations.maxFrequency(nums, k, ops); + assertEquals(1, result); + } + + @Test + public void testAllSameElements() { + int[] nums = { 7, 7, 7, 7 }; + int k = 5, ops = 2; + int result = MaxFrequencyAfterOperations.maxFrequency(nums, k, ops); + assertEquals(4, result); + } + + @Test + public void testLargeK() { + int[] nums = { 1, 100, 200 }; + int k = 200, ops = 2; + int result = MaxFrequencyAfterOperations.maxFrequency(nums, k, ops); + assertEquals(3, result); + } + + @Test + public void testEmptyArray() { + int[] nums = {}; + int result = MaxFrequencyAfterOperations.maxFrequency(nums, 5, 2); + assertEquals(0, result); + } + + @Test + public void testSingleElement() { + int[] nums = { 10 }; + int result = MaxFrequencyAfterOperations.maxFrequency(nums, 3, 1); + assertEquals(1, result); + } + + @Test + public void testNullInputThrowsException() { + assertThrows(NullPointerException.class, () -> MaxFrequencyAfterOperations.maxFrequency(null, 1, 2)); + } +}