LeetCode 1838 - Frequency of the Most Frequent Element
This problem asks us to determine the maximum frequency of an element in an integer array nums after performing at most k increment operations. Each operation allows you to choose an element and increase it by 1.
Difficulty: 🟡 Medium
Topics: Array, Binary Search, Greedy, Sliding Window, Sorting, Prefix Sum
Solution
Problem Understanding
This problem asks us to determine the maximum frequency of an element in an integer array nums after performing at most k increment operations. Each operation allows you to choose an element and increase it by 1. The frequency of an element is the number of times it occurs in the array.
In other words, given the ability to "level up" numbers in the array, we want to maximize the number of identical elements. The input is an array of integers and a single integer k, which represents the total number of increments we can distribute among elements. The output is a single integer, representing the largest possible frequency of any number after these operations.
The constraints are significant: nums can be up to 100,000 elements long, and both nums[i] and k can be as large as 100,000. This indicates that brute-force solutions checking all possibilities would be too slow. Edge cases to consider include arrays with one element, arrays where all elements are equal, arrays that already contain repeated numbers, and scenarios where k is very small or very large relative to the differences between elements.
Approaches
A brute-force approach would involve iterating over every element and attempting to increase smaller elements to match it, keeping track of the number of operations used. For each target element, we could count how many elements can be incremented to reach it within k operations. While this is correct, it has time complexity O(n^2) and would be too slow for large arrays.
The key insight for an optimal solution is that we can sort the array and focus on sliding windows. By sorting, we know that if we want to make a subset of elements equal to the largest element in that subset, it is always optimal to increase the smaller elements in that window to match the largest one. We can then maintain a window [left, right] representing elements we are trying to make equal to nums[right]. The total cost to increase all elements in the window to nums[right] can be efficiently maintained using a running sum.
The optimal solution is therefore a sliding window with prefix sum approach, where the window expands greedily until the total increments needed exceed k, at which point we shrink from the left.
| Approach | Time Complexity | Space Complexity | Notes |
|---|---|---|---|
| Brute Force | O(n^2) | O(1) | Increment each element individually and count frequency |
| Optimal | O(n log n) | O(1) | Sort + sliding window to maintain sum of increments |
Algorithm Walkthrough
- Sort the array. This allows us to consider consecutive sequences of numbers where the smallest can be incremented to match the largest.
- Initialize two pointers,
left = 0and iteraterightover the array. - Maintain a running sum representing the total of elements in the current window.
- For each new
right, calculate the total cost to increase all elements in the window[left, right]tonums[right]using the formulacost = nums[right] * window_size - window_sum. - If the cost exceeds
k, incrementleftto shrink the window and adjust the window sum accordingly. - Update the maximum frequency as
right - left + 1after each step. - Return the maximum frequency found.
Why it works: The invariant maintained is that all elements in the window [left, right] can be incremented to nums[right] without exceeding k operations. By expanding the window greedily and shrinking when necessary, we ensure that at every step we consider the largest possible valid frequency.
Python Solution
from typing import List
class Solution:
def maxFrequency(self, nums: List[int], k: int) -> int:
nums.sort()
left = 0
total = 0
max_freq = 0
for right in range(len(nums)):
total += nums[right]
while (nums[right] * (right - left + 1) - total) > k:
total -= nums[left]
left += 1
max_freq = max(max_freq, right - left + 1)
return max_freq
Implementation Walkthrough: First, we sort nums so that we can process elements in increasing order. We maintain a sliding window [left, right] and a running sum total of elements in that window. For each right, we calculate the operations needed to make all elements equal to nums[right]. If this exceeds k, we shrink the window from the left. After processing each element, we update max_freq to track the largest valid window.
Go Solution
import "sort"
func maxFrequency(nums []int, k int) int {
sort.Ints(nums)
left := 0
total := 0
maxFreq := 0
for right := 0; right < len(nums); right++ {
total += nums[right]
for nums[right]*(right-left+1)-total > k {
total -= nums[left]
left++
}
if right-left+1 > maxFreq {
maxFreq = right - left + 1
}
}
return maxFreq
}
Go-specific notes: Sorting is done using sort.Ints. We carefully use integer arithmetic, and since total is an int, it can hold sums up to 10^10 without overflow in Go. The logic mirrors the Python solution using a sliding window and running sum.
Worked Examples
Example 1: nums = [1,2,4], k = 5
After sorting: [1,2,4]
Window expands:
| left | right | window_sum | cost | max_freq |
|---|---|---|---|---|
| 0 | 0 | 1 | 0 | 1 |
| 0 | 1 | 3 | 1*2 -3=1? wait let's calculate | 2 |
| 0 | 2 | 7 | 4*3 - 7 = 5 | 3 |
All elements can be incremented to 4 with total cost 5 ≤ k. Output: 3.
Example 2: nums = [1,4,8,13], k = 5
After sorting: [1,4,8,13]
Window calculations:
- Right=0: max_freq=1
- Right=1: cost = 4*2 - (1+4)=8-5=3 ≤ 5 → max_freq=2
- Right=2: cost = 8_3 - (1+4+8)=24-13=11 > 5 → shrink left → left=1 → cost=8_2-(4+8)=16-12=4 ≤5 → max_freq=2
- Right=3: cost=13_3-(4+8+13)=39-25=14>5 → shrink left → left=2 → cost=13_2-(8+13)=26-21=5 → max_freq=2
Output: 2
Complexity Analysis
| Measure | Complexity | Explanation |
|---|---|---|
| Time | O(n log n) | Sorting takes O(n log n), sliding window is O(n) |
| Space | O(1) | Only pointers and running sum are used |
Sorting dominates time complexity. The sliding window ensures we only traverse each element once for left and right pointers.
Test Cases
# Provided examples
assert Solution().maxFrequency([1,2,4], 5) == 3 # all become 4
assert Solution().maxFrequency([1,4,8,13], 5) == 2
assert Solution().maxFrequency([3,9,6], 2) == 1
# Edge cases
assert Solution().maxFrequency([1], 100) == 1 # single element
assert Solution().maxFrequency([1,1,1], 0) == 3 # already equal
assert Solution().maxFrequency([1,2,3,4,5], 10) == 5 # enough k to make all equal
assert Solution().maxFrequency([1,1,2,2,3,3], 3) == 4 # partial increments
assert Solution().maxFrequency([5,1,1,1], 2) == 3 # max frequency in middle
# Stress case
assert Solution().maxFrequency(list(range(1, 100001)), 100000) == 2 # incremental only possible for neighbors
| Test | Why |
|---|---|
| [1,2,4], k=5 | Basic functionality with moderate k |
| [1,4,8,13], k=5 | Multiple optimal paths |
| [3,9,6], k=2 | Insufficient k |
| [1], k=100 | Single element edge |
| [1,1,1], k=0 | Already equal elements |
| [1,2,3,4,5], k=10 | Enough k to equalize all |
| [1,1,2,2,3,3], k=3 | Partial increment scenario |
| [5,1,1,1], k=2 | Middle window increments |
Edge Cases
Single element array: Arrays of length 1 should return 1 regardless of k, as