2968. Apply Operations to Maximize Frequency Score
Problem Description
You have an array of integers nums (0-indexed) and an integer k representing the maximum number of operations you can perform.
In each operation, you can:
- Select any index
iin the array - Either increase or decrease
nums[i]by 1
Your goal is to maximize the frequency of the most frequent element in the final array. The frequency of an element is simply how many times it appears in the array.
For example, if you have nums = [1, 2, 4] and k = 5:
- You could change the array to
[2, 2, 2]by decreasing 4 twice (2 operations) and increasing 1 once (1 operation), using 3 operations total - This gives you a maximum frequency of 3 (the element 2 appears 3 times)
The key insight is that you want to make as many elements equal as possible within your operation budget k. Each operation moves an element one step closer to your target value, so elements that are already close to each other will require fewer operations to become equal.
The solution uses binary search to find the maximum possible frequency. For each candidate frequency, it checks if it's possible to create a subarray of that length where all elements can be made equal using at most k operations. The optimal strategy is to change all elements to the median value of the subarray, as this minimizes the total number of operations needed.
Intuition
The first key observation is that if we want to make multiple elements equal, it's most efficient to work with elements that are already close to each other in value. This suggests we should sort the array first, as consecutive elements in a sorted array have the smallest differences.
Once sorted, we realize that to maximize frequency, we should focus on making a contiguous segment of the sorted array equal. Why? Because if we try to make non-contiguous elements equal, we'd be wasting operations on elements in between that we're not including.
Now, what value should we change all elements in a segment to? The median is optimal. Consider a segment [1, 2, 5]:
- If we change all to 1: operations needed =
0 + 1 + 4 = 5 - If we change all to 2: operations needed =
1 + 0 + 3 = 4 - If we change all to 5: operations needed =
4 + 3 + 0 = 7
The median minimizes the total distance (and thus operations) needed.
The next insight is about the search strategy. If we can achieve a frequency of x (meaning we can make x elements equal), then we can definitely achieve any frequency less than x - we'd just use fewer elements. This monotonic property screams binary search!
So our approach becomes:
- Binary search on the answer (the maximum frequency)
- For each candidate frequency
mid, check if it's achievable - To check if frequency
midis achievable, try all possible windows of sizemidin the sorted array - For each window, calculate the cost to make all elements equal to the median
- If any window has cost ≤
k, then frequencymidis achievable
The cost calculation uses prefix sums for efficiency. For a window from index i to j, we need:
- Elements below the median to increase to reach the median
- Elements above the median to decrease to reach the median
This gives us the formula: left + right where:
left= sum of differences between median and elements below itright= sum of differences between elements above median and the median itself
Learn more about Binary Search, Prefix Sum, Sorting and Sliding Window patterns.
Solution Implementation
1class Solution:
2 def maxFrequencyScore(self, nums: List[int], k: int) -> int:
3 # Sort the array for efficient median-based calculations
4 nums.sort()
5
6 # Create prefix sum array for range sum queries
7 # prefix_sum[i] represents sum of elements from nums[0] to nums[i-1]
8 prefix_sum = list(accumulate(nums, initial=0))
9 n = len(nums)
10
11 def feasible(window_size: int) -> bool:
12 """Check if we can make window_size elements equal with at most k operations."""
13 # Try all possible windows of the given size
14 for window_start in range(n - window_size + 1):
15 window_end = window_start + window_size
16
17 # Find the median element's index in current window
18 median_index = (window_start + window_end) // 2
19 median_value = nums[median_index]
20
21 # Calculate cost to change all elements left of median to median value
22 left_count = median_index - window_start
23 left_sum = prefix_sum[median_index] - prefix_sum[window_start]
24 left_cost = left_count * median_value - left_sum
25
26 # Calculate cost to change all elements right of median to median value
27 right_count = window_end - median_index
28 right_sum = prefix_sum[window_end] - prefix_sum[median_index]
29 right_cost = right_sum - right_count * median_value
30
31 # Check if total cost is within budget k
32 if left_cost + right_cost <= k:
33 return True
34 return False
35
36 # Binary search for maximum feasible frequency
37 # Pattern: true, true, ..., true, false, false (find last true)
38 left, right = 1, n
39 last_true_index = 0
40
41 while left <= right:
42 mid = (left + right) // 2
43 if feasible(mid):
44 last_true_index = mid
45 left = mid + 1 # Try larger frequency
46 else:
47 right = mid - 1 # Try smaller frequency
48
49 return last_true_index
501class Solution {
2 private int[] nums;
3 private long[] prefixSum;
4 private long k;
5
6 public int maxFrequencyScore(int[] nums, long k) {
7 // Sort the array to enable efficient calculation of operations needed
8 Arrays.sort(nums);
9 this.nums = nums;
10 this.k = k;
11 int n = nums.length;
12
13 // Build prefix sum array for quick range sum queries
14 prefixSum = new long[n + 1];
15 for (int i = 1; i <= n; i++) {
16 prefixSum[i] = prefixSum[i - 1] + nums[i - 1];
17 }
18
19 // Binary search for maximum feasible frequency
20 // Pattern: true, true, ..., true, false, false (find last true)
21 int left = 1;
22 int right = n;
23 int lastTrueIndex = 0;
24
25 while (left <= right) {
26 int mid = left + (right - left) / 2;
27 if (feasible(mid)) {
28 lastTrueIndex = mid;
29 left = mid + 1; // Try larger frequency
30 } else {
31 right = mid - 1; // Try smaller frequency
32 }
33 }
34
35 return lastTrueIndex;
36 }
37
38 private boolean feasible(int windowSize) {
39 int n = nums.length;
40 // Try all possible windows of the given size
41 for (int startIdx = 0; startIdx <= n - windowSize; startIdx++) {
42 int endIdx = startIdx + windowSize;
43
44 // Choose median as target value (optimal for minimizing total operations)
45 int medianIdx = (startIdx + endIdx) / 2;
46 int targetValue = nums[medianIdx];
47
48 // Calculate operations needed for left half (elements smaller than median)
49 long leftOperations = (long)(medianIdx - startIdx) * targetValue -
50 (prefixSum[medianIdx] - prefixSum[startIdx]);
51
52 // Calculate operations needed for right half (elements larger than median)
53 long rightOperations = (prefixSum[endIdx] - prefixSum[medianIdx]) -
54 (long)(endIdx - medianIdx) * targetValue;
55
56 // Check if total operations needed is within budget k
57 if (leftOperations + rightOperations <= k) {
58 return true;
59 }
60 }
61 return false;
62 }
63}
641class Solution {
2public:
3 int maxFrequencyScore(vector<int>& nums, long long k) {
4 // Sort the array to group similar values together
5 sort(nums.begin(), nums.end());
6
7 int n = nums.size();
8
9 // Build prefix sum array for efficient range sum queries
10 vector<long long> prefixSum(n + 1, 0);
11 for (int i = 1; i <= n; i++) {
12 prefixSum[i] = prefixSum[i - 1] + nums[i - 1];
13 }
14
15 // Feasibility check: can we make windowSize elements equal with at most k operations?
16 auto feasible = [&](int windowSize) -> bool {
17 for (int i = 0; i <= n - windowSize; i++) {
18 int j = i + windowSize;
19
20 // Choose median as the target value
21 int medianIndex = (i + j) / 2;
22 int targetValue = nums[medianIndex];
23
24 // Calculate cost for left half
25 long long leftCost = (long long)(medianIndex - i) * targetValue -
26 (prefixSum[medianIndex] - prefixSum[i]);
27
28 // Calculate cost for right half
29 long long rightCost = (prefixSum[j] - prefixSum[medianIndex]) -
30 (long long)(j - medianIndex) * targetValue;
31
32 if (leftCost + rightCost <= k) {
33 return true;
34 }
35 }
36 return false;
37 };
38
39 // Binary search for maximum feasible frequency
40 // Pattern: true, true, ..., true, false, false (find last true)
41 int left = 1;
42 int right = n;
43 int lastTrueIndex = 0;
44
45 while (left <= right) {
46 int mid = left + (right - left) / 2;
47 if (feasible(mid)) {
48 lastTrueIndex = mid;
49 left = mid + 1; // Try larger frequency
50 } else {
51 right = mid - 1; // Try smaller frequency
52 }
53 }
54
55 return lastTrueIndex;
56 }
57};
581/**
2 * Finds the maximum frequency score achievable by performing at most k operations
3 * @param nums - Array of numbers to process
4 * @param k - Maximum number of operations allowed
5 * @returns Maximum frequency score (length of subarray that can be made equal)
6 */
7function maxFrequencyScore(nums: number[], k: number): number {
8 // Sort the array in ascending order
9 nums.sort((a, b) => a - b);
10
11 const n: number = nums.length;
12
13 // Build prefix sum array for quick range sum calculations
14 const prefixSum: number[] = Array(n + 1).fill(0);
15 for (let i = 1; i <= n; i++) {
16 prefixSum[i] = prefixSum[i - 1] + nums[i - 1];
17 }
18
19 // Feasibility check: can we make windowSize elements equal with at most k operations?
20 const feasible = (windowSize: number): boolean => {
21 for (let startIdx = 0; startIdx <= n - windowSize; startIdx++) {
22 const endIdx = startIdx + windowSize;
23
24 // Choose the median element as the target value
25 const medianIdx = Math.floor((startIdx + endIdx) / 2);
26 const targetValue = nums[medianIdx];
27
28 // Calculate operations needed for elements left of median
29 const leftOperations = (medianIdx - startIdx) * targetValue -
30 (prefixSum[medianIdx] - prefixSum[startIdx]);
31
32 // Calculate operations needed for elements right of median
33 const rightOperations = (prefixSum[endIdx] - prefixSum[medianIdx]) -
34 (endIdx - medianIdx) * targetValue;
35
36 if (leftOperations + rightOperations <= k) {
37 return true;
38 }
39 }
40 return false;
41 };
42
43 // Binary search for maximum feasible frequency
44 // Pattern: true, true, ..., true, false, false (find last true)
45 let left = 1;
46 let right = n;
47 let lastTrueIndex = 0;
48
49 while (left <= right) {
50 const mid = Math.floor((left + right) / 2);
51 if (feasible(mid)) {
52 lastTrueIndex = mid;
53 left = mid + 1; // Try larger frequency
54 } else {
55 right = mid - 1; // Try smaller frequency
56 }
57 }
58
59 return lastTrueIndex;
60}
61Solution Approach
Let's walk through the implementation step by step:
1. Sorting and Prefix Sum Setup
First, we sort the array to group similar values together:
nums.sort()
Then we build a prefix sum array for efficient range sum queries:
s = list(accumulate(nums, initial=0))
This allows us to calculate the sum of any subarray nums[i:j] as s[j] - s[i] in O(1) time.
2. Binary Search Framework
We binary search on the possible frequency values:
l, r = 0, n while l < r: mid = (l + r + 1) >> 1
l = 0: minimum possible frequencyr = n: maximum possible frequency (all elements become equal)mid = (l + r + 1) >> 1: middle value with upward rounding
3. Checking Feasibility of a Frequency
For each candidate frequency mid, we check all possible windows of size mid:
for i in range(n - mid + 1):
j = i + mid
x = nums[(i + j) // 2] # median element
4. Calculating Operations Needed
For each window [i, j), we calculate the cost to make all elements equal to the median x:
left = ((i + j) // 2 - i) * x - (s[(i + j) // 2] - s[i]) right = (s[j] - s[(i + j) // 2]) - ((j - (i + j) // 2) * x)
Breaking down the formulas:
-
Left cost: Elements from index
ito(i+j)//2 - 1need to increase tox- Number of elements:
(i + j) // 2 - i - Target sum if all were
x:((i + j) // 2 - i) * x - Current sum:
s[(i + j) // 2] - s[i] - Operations needed: target sum - current sum
- Number of elements:
-
Right cost: Elements from index
(i+j)//2 + 1toj-1need to decrease tox- Current sum:
s[j] - s[(i + j) // 2] - Target sum if all were
x:(j - (i + j) // 2) * x - Operations needed: current sum - target sum
- Current sum:
5. Binary Search Decision
If any window can be transformed with at most k operations:
if left + right <= k: ok = True break
Based on feasibility:
- If feasible (
ok = True): This frequency works, try larger (l = mid) - If not feasible: This frequency is too large, try smaller (
r = mid - 1)
Time Complexity: O(n² log n)
- Binary search: O(log n) iterations
- Each iteration checks O(n) windows
- Each window check: O(1) using prefix sums
Space Complexity: O(n) for the prefix sum array
Ready to land your dream job?
Unlock your dream job with a 5-minute evaluator for a personalized learning plan!
Start EvaluatorExample Walkthrough
Let's walk through a concrete example with nums = [1, 4, 8, 13] and k = 5.
Step 1: Sort and Build Prefix Sum
- Array is already sorted:
[1, 4, 8, 13] - Prefix sum array:
s = [0, 1, 5, 13, 26]- This allows us to quickly calculate range sums
Step 2: Binary Search on Frequency
We'll binary search between frequency 1 and 4 (array length).
First iteration: mid = 2
-
Can we make 2 elements equal with ≤ 5 operations?
-
Check all windows of size 2:
Window
[1, 4](indices 0-1):- Median element:
nums[0] = 1 - Cost to make both equal to 1: We need to decrease 4 to 1
- Operations = 3 ✓ (≤ 5, so frequency 2 is achievable)
- Median element:
Second iteration: mid = 3
-
Can we make 3 elements equal with ≤ 5 operations?
-
Check all windows of size 3:
Window
[1, 4, 8](indices 0-2):- Median element:
nums[1] = 4 - Cost calculation:
- Left side: Change 1 to 4 = 3 operations
- Right side: Change 8 to 4 = 4 operations
- Total = 7 operations ✗ (> 5)
Window
[4, 8, 13](indices 1-3):- Median element:
nums[2] = 8 - Cost calculation:
- Left side: Change 4 to 8 = 4 operations
- Right side: Change 13 to 8 = 5 operations
- Total = 9 operations ✗ (> 5)
Frequency 3 is not achievable with k=5.
- Median element:
Binary Search Continues:
- Since frequency 3 failed, we search lower
- We already know frequency 2 works
- Binary search converges to answer: 2
Verification of the Answer:
With k=5 operations, we can transform [1, 4, 8, 13] to have at most frequency 2:
- One possibility: Change 4 to 1 (3 operations) →
[1, 1, 8, 13] - Another possibility: Change 8 to 4 (4 operations) →
[1, 4, 4, 13]
The maximum frequency achievable is 2.
Template Solution (Last True Pattern)
def maxFrequencyScore(self, nums: List[int], k: int) -> int:
nums.sort()
n = len(nums)
prefix_sum = list(accumulate(nums, initial=0))
def feasible(window_size: int) -> bool:
for i in range(n - window_size + 1):
j = i + window_size
median_idx = (i + j) // 2
median = nums[median_idx]
left_cost = (median_idx - i) * median - (prefix_sum[median_idx] - prefix_sum[i])
right_cost = (prefix_sum[j] - prefix_sum[median_idx]) - (j - median_idx) * median
if left_cost + right_cost <= k:
return True
return False
# Binary search: find last true (maximum feasible frequency)
left, right = 1, n
last_true_index = 0
while left <= right:
mid = (left + right) // 2
if feasible(mid):
last_true_index = mid
left = mid + 1 # Try larger
else:
right = mid - 1 # Try smaller
return last_true_index
Time and Space Complexity
Time Complexity: O(n × log n)
The time complexity consists of two main parts:
- Sorting the array takes
O(n log n)time - Binary search on the answer space: The binary search runs for
O(log n)iterations (searching from 0 to n for the maximum frequency). In each iteration, we check all possible subarrays of lengthmid, which takesO(n)time. Therefore, this part contributesO(n × log n)time.
Since both operations are O(n × log n), the overall time complexity is O(n × log n).
Space Complexity: O(n)
The space complexity comes from:
- The prefix sum array
swhich storesn+1elements, requiringO(n)space - The sorting operation may require
O(log n)space for the recursion stack in the worst case (depending on the sorting algorithm implementation) - Other variables like
l,r,mid,i,j,x,left,right, andokuseO(1)space
The dominant term is O(n) from the prefix sum array, so the overall space complexity is O(n).
Learn more about how to find time and space complexity quickly.
Common Pitfalls
1. Using Wrong Binary Search Template Variant
This is a maximization problem (find largest feasible frequency), which requires the "last true" pattern. Using the "first true" pattern will give incorrect results.
The Issue:
# WRONG: "first true" pattern (for minimization) first_true_index = -1 while left <= right: mid = (left + right) // 2 if feasible(mid): first_true_index = mid right = mid - 1 # Wrong direction! else: left = mid + 1
Solution: Use the "last true" pattern for maximization:
# CORRECT: "last true" pattern (for maximization) last_true_index = 0 while left <= right: mid = (left + right) // 2 if feasible(mid): last_true_index = mid left = mid + 1 # Try larger values else: right = mid - 1 # Try smaller values
2. Incorrect Median Index Calculation for Even-Length Windows
One of the most common mistakes is mishandling the median index calculation when the window has an even number of elements.
The Issue:
For even-length windows, there are two middle elements. The formula (i + j) // 2 gives us the upper median index. This inconsistency can lead to incorrect cost calculations.
Solution: Be explicit about which median you're using and ensure consistency:
# For a window [window_start, window_end) median_index = (window_start + window_end) // 2
3. Confusion with Prefix Sum Boundaries
The prefix sum array uses initial=0, making prefix_sum[i] represent the sum of elements from index 0 to i-1 (exclusive of i).
The Issue:
# Wrong: sum from i to j inclusive wrong_sum = prefix_sum[j] - prefix_sum[i] # This actually gives sum from i to j-1 # Correct: sum from i to j inclusive correct_sum = prefix_sum[j + 1] - prefix_sum[i]
4. Not Defining a Clear Feasible Function
Mixing the feasibility check into the binary search loop makes the code harder to reason about and more error-prone.
Solution: Extract the feasibility check into a separate function:
def feasible(window_size: int) -> bool:
"""Check if we can make window_size elements equal with at most k operations."""
for window_start in range(n - window_size + 1):
# ... check each window
if cost <= k:
return True
return False
5. Integer Overflow in Cost Calculations
For large arrays with large values, the cost calculations might overflow in languages with fixed integer sizes.
Solution (for languages with overflow concerns):
Use long long in C++ or long in Java for storing sums and products.
What's the output of running the following function using the following tree as input?

1def serialize(root):
2 res = []
3 def dfs(root):
4 if not root:
5 res.append('x')
6 return
7 res.append(root.val)
8 dfs(root.left)
9 dfs(root.right)
10 dfs(root)
11 return ' '.join(res)
121import java.util.StringJoiner;
2
3public static String serialize(Node root) {
4 StringJoiner res = new StringJoiner(" ");
5 serializeDFS(root, res);
6 return res.toString();
7}
8
9private static void serializeDFS(Node root, StringJoiner result) {
10 if (root == null) {
11 result.add("x");
12 return;
13 }
14 result.add(Integer.toString(root.val));
15 serializeDFS(root.left, result);
16 serializeDFS(root.right, result);
17}
181function serialize(root) {
2 let res = [];
3 serialize_dfs(root, res);
4 return res.join(" ");
5}
6
7function serialize_dfs(root, res) {
8 if (!root) {
9 res.push("x");
10 return;
11 }
12 res.push(root.val);
13 serialize_dfs(root.left, res);
14 serialize_dfs(root.right, res);
15}
16Recommended Readings
https assets algo monster cover_photos Binary_Search svg Binary Search Intuition Binary search is an efficient array search algorithm It works by narrowing down the search range by half each time If you have looked up a word in a physical dictionary you've already used binary search in real life Let's
Prefix Sum Given an array of integers arr and a target value find a subarray that sums to the target Return the start and end indices of the subarray Input arr 1 20 3 30 5 4 target 7 Output 1 4 Explanation The subarray 20 3 30 from index 1 inclusive
Sorting Summary Comparisons We presented quite a few sorting algorithms and it is essential to know the advantages and disadvantages of each one The basic algorithms are easy to visualize and easy to learn for beginner programmers because of their simplicity As such they will suffice if you don't know any advanced
Want a Structured Path to Master System Design Too? Don’t Miss This!