Facebook Pixel

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.

Quick Interview Experience
Help others by sharing your interview experience
Have you seen this problem before?

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
37
1class 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}
47
1class 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};
42
1function 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}
39

Solution 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 Evaluator

Example 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] = 0
    • s[1] = 0 + 1 = 1
    • s[2] = 1 + 4 = 5
    • s[3] = 5 + 8 = 13
    • s[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
    • 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
    • Frequency 3 is NOT achievable, so r = 2

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! ✓
    • Frequency 2 is achievable, so l = 2

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 nums takes O(n × log n) time
  • Binary search runs O(log n) iterations, where each iteration calls the check function
  • The check function iterates through at most n elements in its for loop, taking O(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 s stores n + 1 elements, requiring O(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)
Loading...
Discover Your Strengths and Weaknesses: Take Our 5-Minute Quiz to Tailor Your Study Plan:

What are the most two important steps in writing a depth first search function? (Select 2)


Recommended Readings

Want a Structured Path to Master System Design Too? Don’t Miss This!

Load More