1838. Frequency of the Most Frequent Element
Problem Description
You are given an integer array nums and an integer k. The frequency of an element is the number of times it appears in the array.
In one operation, you can select any index in nums and increment the element at that index by 1. You can perform at most k operations total.
Your goal is to find the maximum possible frequency of any element after performing at most k operations.
For example, if nums = [1, 2, 4] and k = 5, you could increment the first element three times (1→2→3→4) and the second element twice (2→3→4), using all 5 operations. This would give you the array [4, 4, 4], where the element 4 has a frequency of 3, which is the maximum possible frequency.
The key insight is that you want to make multiple elements equal to maximize frequency, and since you can only increment values, you'll typically want to increase smaller elements to match a larger target value. The challenge is determining which elements to modify and what target value to choose to maximize the frequency while staying within the operation budget k.
Intuition
Let's think about what happens when we try to maximize frequency. We want to make as many elements equal as possible using our limited operations.
First, consider which value we should target. Since we can only increment elements (never decrement), if we want to make multiple elements equal, we must choose a target value that's at least as large as all the elements we're modifying. The most efficient choice is to pick one of the existing elements as our target - specifically, the largest element in the group we're modifying. Why? Because if we pick any value larger than the maximum element, we're wasting operations by incrementing that maximum element unnecessarily.
Next, which elements should we modify? If we sort the array first, it becomes clear that we should pick consecutive elements. For instance, if we have [1, 2, 5, 8, 10] and want to make three elements equal to 5, it makes sense to choose [1, 2, 5] rather than [1, 2, 8] because the closer the elements are to our target, the fewer operations we need.
Now, how do we find the maximum frequency? We could check every possible window size (frequency), but that would be inefficient. Here's a key observation: if we can achieve a frequency of m, then we can definitely achieve any frequency less than m (we just use fewer elements). This monotonic property suggests binary search!
For binary search, we need to check if a given frequency m is achievable. We slide a window of size m through the sorted array and for each window, calculate the cost to make all elements equal to the maximum element in that window. If the window ends at index i, the maximum element is nums[i-1], and the cost is nums[i-1] * m - sum(window). We use a prefix sum array to quickly calculate the sum of any window.
The binary search finds the largest frequency where at least one window of that size can be transformed within our operation budget k.
Learn more about Greedy, Binary Search, Prefix Sum, Sorting and Sliding Window patterns.
Solution Implementation
1class Solution:
2 def maxFrequency(self, nums: List[int], k: int) -> int:
3 nums.sort()
4 n = len(nums)
5
6 # Build prefix sum array for quick range sum queries
7 prefix_sum = [0]
8 for num in nums:
9 prefix_sum.append(prefix_sum[-1] + num)
10
11 def cannot_achieve_frequency(frequency: int) -> bool:
12 """Check if frequency is NOT achievable (exceeds budget for all windows)."""
13 for right in range(frequency, n + 1):
14 left = right - frequency
15 window_sum = prefix_sum[right] - prefix_sum[left]
16 target_element = nums[right - 1]
17 operations_needed = target_element * frequency - window_sum
18 if operations_needed <= k:
19 return False # This frequency IS achievable
20 return True # Cannot achieve this frequency
21
22 # Binary search for the first frequency that cannot be achieved
23 # Answer will be first_true_index - 1 (last achievable frequency)
24 left, right = 1, n + 1
25 first_true_index = -1
26
27 while left <= right:
28 mid = (left + right) // 2
29 if cannot_achieve_frequency(mid):
30 first_true_index = mid
31 right = mid - 1
32 else:
33 left = mid + 1
34
35 # If first_true_index is found, answer is the frequency just before it
36 return first_true_index - 1 if first_true_index != -1 else n
371class Solution {
2 private int[] nums;
3 private long[] prefixSum;
4 private int maxOperations;
5
6 public int maxFrequency(int[] nums, int k) {
7 this.maxOperations = k;
8 this.nums = nums;
9 Arrays.sort(nums);
10 int n = nums.length;
11
12 prefixSum = new long[n + 1];
13 for (int i = 1; i <= n; i++) {
14 prefixSum[i] = prefixSum[i - 1] + nums[i - 1];
15 }
16
17 // Binary search for the first frequency that cannot be achieved
18 int left = 1;
19 int right = n + 1;
20 int firstTrueIndex = -1;
21
22 while (left <= right) {
23 int mid = left + (right - left) / 2;
24 if (cannotAchieveFrequency(mid)) {
25 firstTrueIndex = mid;
26 right = mid - 1;
27 } else {
28 left = mid + 1;
29 }
30 }
31
32 return firstTrueIndex != -1 ? firstTrueIndex - 1 : n;
33 }
34
35 private boolean cannotAchieveFrequency(int targetFreq) {
36 if (targetFreq > nums.length) return true;
37 for (int i = targetFreq; i <= nums.length; i++) {
38 long currentSum = prefixSum[i] - prefixSum[i - targetFreq];
39 long targetSum = 1L * nums[i - 1] * targetFreq;
40 if (targetSum - currentSum <= maxOperations) {
41 return false;
42 }
43 }
44 return true;
45 }
46}
471class Solution {
2public:
3 int maxFrequency(vector<int>& nums, int k) {
4 int n = nums.size();
5 sort(nums.begin(), nums.end());
6
7 vector<long long> prefixSum(n + 1, 0);
8 for (int i = 1; i <= n; ++i) {
9 prefixSum[i] = prefixSum[i - 1] + nums[i - 1];
10 }
11
12 auto cannotAchieveFrequency = [&](int freq) -> bool {
13 if (freq > n) return true;
14 for (int i = freq; i <= n; ++i) {
15 long long targetSum = 1LL * nums[i - 1] * freq;
16 long long currentSum = prefixSum[i] - prefixSum[i - freq];
17 if (targetSum - currentSum <= k) {
18 return false;
19 }
20 }
21 return true;
22 };
23
24 // Binary search for the first frequency that cannot be achieved
25 int left = 1;
26 int right = n + 1;
27 int firstTrueIndex = -1;
28
29 while (left <= right) {
30 int mid = left + (right - left) / 2;
31 if (cannotAchieveFrequency(mid)) {
32 firstTrueIndex = mid;
33 right = mid - 1;
34 } else {
35 left = mid + 1;
36 }
37 }
38
39 return firstTrueIndex != -1 ? firstTrueIndex - 1 : n;
40 }
41};
421function maxFrequency(nums: number[], k: number): number {
2 const n = nums.length;
3 nums.sort((a, b) => a - b);
4
5 const prefixSum: number[] = Array(n + 1).fill(0);
6 for (let i = 1; i <= n; ++i) {
7 prefixSum[i] = prefixSum[i - 1] + nums[i - 1];
8 }
9
10 const cannotAchieveFrequency = (freq: number): boolean => {
11 if (freq > n) return true;
12 for (let i = freq; i <= n; ++i) {
13 const targetSum = nums[i - 1] * freq;
14 const windowSum = prefixSum[i] - prefixSum[i - freq];
15 if (targetSum - windowSum <= k) {
16 return false;
17 }
18 }
19 return true;
20 };
21
22 // Binary search for the first frequency that cannot be achieved
23 let left = 1;
24 let right = n + 1;
25 let firstTrueIndex = -1;
26
27 while (left <= right) {
28 const mid = Math.floor((left + right) / 2);
29 if (cannotAchieveFrequency(mid)) {
30 firstTrueIndex = mid;
31 right = mid - 1;
32 } else {
33 left = mid + 1;
34 }
35 }
36
37 return firstTrueIndex !== -1 ? firstTrueIndex - 1 : n;
38}
39Solution Approach
The solution uses the binary search template to find the maximum frequency. The key insight is to search for the first frequency that cannot be achieved, then return the frequency just before it.
Step 1: Sort the array and compute prefix sums
nums.sort() prefix_sum = [0] for num in nums: prefix_sum.append(prefix_sum[-1] + num)
Step 2: Define the feasibility function
The function cannot_achieve_frequency(freq) returns True if the frequency cannot be achieved (i.e., all windows exceed the budget):
def cannot_achieve_frequency(frequency: int) -> bool:
for right in range(frequency, n + 1):
left = right - frequency
window_sum = prefix_sum[right] - prefix_sum[left]
target_element = nums[right - 1]
operations_needed = target_element * frequency - window_sum
if operations_needed <= k:
return False # This frequency IS achievable
return True # Cannot achieve this frequency
This creates the pattern: false, false, ..., false, true, true, ...
- Small frequencies are achievable → return
false - At some point, frequencies become too large → return
true
Step 3: Binary search template
left, right = 1, n + 1 first_true_index = -1 while left <= right: mid = (left + right) // 2 if cannot_achieve_frequency(mid): first_true_index = mid right = mid - 1 else: left = mid + 1 return first_true_index - 1 if first_true_index != -1 else n
The answer is first_true_index - 1 because we want the last achievable frequency.
The time complexity is O(n log n) for sorting plus O(n log n) for binary search. The space complexity is 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 compute prefix sums
- Array is already sorted:
[1, 4, 8, 13] - Prefix sums:
s = [0, 1, 5, 13, 26]s[0] = 0s[1] = 0 + 1 = 1s[2] = 1 + 4 = 5s[3] = 5 + 8 = 13s[4] = 13 + 13 = 26
Step 2: Binary search setup
- Search range:
l = 1, r = 4(minimum frequency 1, maximum frequency 4)
Step 3: Binary search execution
First iteration: mid = (1 + 4 + 1) / 2 = 3
- Check if frequency 3 is achievable:
- Window 1: indices 0-2, elements
[1, 4, 8]- Target:
nums[2] = 8 - Cost:
8 × 3 - (s[3] - s[0]) = 24 - 13 = 11 - 11 > 5, too expensive
- Target:
- Window 2: indices 1-3, elements
[4, 8, 13]- Target:
nums[3] = 13 - Cost:
13 × 3 - (s[4] - s[1]) = 39 - 25 = 14 - 14 > 5, too expensive
- Target:
- Frequency 3 is NOT achievable, so
r = 2
- Window 1: indices 0-2, elements
Second iteration: mid = (1 + 2 + 1) / 2 = 2
- Check if frequency 2 is achievable:
- Window 1: indices 0-1, elements
[1, 4]- Target:
nums[1] = 4 - Cost:
4 × 2 - (s[2] - s[0]) = 8 - 5 = 3 - 3 ≤ 5, achievable! ✓
- Target:
- Frequency 2 is achievable, so
l = 2
- Window 1: indices 0-1, elements
Third iteration: l = 2, r = 2, loop exits
Result: Maximum frequency = 2
We can verify: With 3 operations, we can increment 1→2→3→4, giving us [4, 4, 8, 13] with frequency 2 for element 4.
Time and Space Complexity
Time Complexity: O(n × log n)
The time complexity consists of two main parts:
- Sorting the array
numstakesO(n × log n)time - Binary search runs
O(log n)iterations, where each iteration calls thecheckfunction - The
checkfunction iterates through at mostnelements in its for loop, takingO(n)time - Therefore, the binary search portion takes
O(n × log n)time - The overall time complexity is
O(n × log n) + O(n × log n) = O(n × log n)
Space Complexity: O(n)
The space complexity analysis:
- The prefix sum array
sstoresn + 1elements, requiringO(n)space - The sorting operation may require
O(log n)space for the recursion stack (depending on the sorting algorithm implementation) - Other variables use constant space
O(1) - The dominant space usage is the prefix sum array, giving an overall space complexity of
O(n)
Learn more about how to find time and space complexity quickly.
Common Pitfalls
1. Using Wrong Binary Search Template Variant
Pitfall: Using while left < right with left = mid instead of the standard template.
Wrong Implementation:
while left_bound < right_bound: mid = (left_bound + right_bound + 1) // 2 if can_achieve_frequency(mid): left_bound = mid else: right_bound = mid - 1 return left_bound
Correct Implementation (using template):
left, right = 1, n + 1 first_true_index = -1 while left <= right: mid = (left + right) // 2 if cannot_achieve_frequency(mid): first_true_index = mid right = mid - 1 else: left = mid + 1 return first_true_index - 1 if first_true_index != -1 else n
2. Integer Overflow in Cost Calculation
Pitfall: The multiplication target_element * frequency can cause overflow in languages with fixed integer sizes.
Solution: Use long/int64 data types in Java/C++.
3. Off-by-One Error in Window Indexing
Pitfall: Incorrectly calculating window boundaries or the target element.
Solution: Ensure correct window boundaries:
for right in range(frequency, n + 1): # right is exclusive end
left = right - frequency # left is inclusive start
window_sum = prefix_sum[right] - prefix_sum[left]
target_element = nums[right - 1] # Maximum element in sorted window
4. Forgetting to Sort the Array
Pitfall: The algorithm relies on the array being sorted.
Solution: Always sort the array first: nums.sort()
5. Incorrect Prefix Sum Initialization
Solution: Initialize with 0 for easier range sum calculation:
prefix_sum = [0] for num in nums: prefix_sum.append(prefix_sum[-1] + num)
What are the most two important steps in writing a depth first search function? (Select 2)
Recommended Readings
Greedy Introduction div class responsive iframe iframe src https www youtube com embed WTslqPbj7I title YouTube video player frameborder 0 allow accelerometer autoplay clipboard write encrypted media gyroscope picture in picture web share allowfullscreen iframe div When do we use greedy Greedy algorithms tend to solve optimization problems Typically they will ask you to calculate the max min of some value Commonly you may see this phrased in the problem as max min longest shortest largest smallest etc These keywords can be identified by just scanning
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
Want a Structured Path to Master System Design Too? Don’t Miss This!