Leetcode 1224. Maximum Equal Frequency

Problem Explanation

The problem is about finding the longest possible prefix of an input array of integers such that removing just one element will result in each number appearing the same number of times in that prefix.

For example, in the case of the array [2,2,1,1,5,3,3,5], the longest possible prefix where we can remove one element to have each number appear the same number of times is [2,2,1,1,5,3,3]. If we remove the 5, we get [2,2,1,1,3,3] where each number appears twice.

The challenge is to find an efficient way to identify such prefixes from the given array.

Solution Approach

The solution uses a hash map called 'count' to track the frequency of each number in the prefix. It also uses another hash map called 'freq' to track how many numbers have each frequency.

It loops over each number in the input array updating 'count' and 'freq' maps and identifying if a valid prefix exists at each step. There are three ways a valid prefix can exist:

  1. All numbers appear exactly once (max frecuency is 1)
  2. All numbers appear maxFreq times and one number appears once.
  3. All numbers appear maxFreq times and one number appears maxFreq+1 times.

After checking and updating values in the maps, it checks these three conditions for a valid prefix and whenever it finds a valid prefix it updates the value of 'ans' variable with the prefix length.

Let's consider an example ([1,1,1,2,2,2,3,3,3,4,4,4,5]) and go step by step:

  • We begin by iterating through the array and updating count and freq maps for each number.
  • For the first three 1's, maxFreq is 3 and there's only one number with frequency 3.
  • We then encounter 2 and its frequency also reaches 3 and again there's only one number with frequency 3.
  • The same happens with 3 and maxFreq remains 3 but now there are three numbers with frequency 3.
  • When we process the first 4, maxFreq remains unchanged but there's an increase in the number of elements with frequency 1.
  • The process repeats for the next two 4's and finally when we encounter a 5, the condition (maxFreq - 1) * (freq[maxFreq - 1] + 1) equals the current index i and we update 'ans' with current index + 1.

Python Solution

1
2python
3class Solution:
4    def maxEqualFreq(self, nums: List[int]) -> int:
5        count = {}
6        freq = {}
7        ans = 0
8        maxFreq = 0
9        for i, num in enumerate(nums):
10            if num in count:
11                freq[count[num]] -= 1
12                count[num] += 1
13            else:
14                count[num] = 1
15            if count[num] in freq:
16                freq[count[num]] += 1
17            else:
18                freq[count[num]] = 1
19            maxFreq = max(maxFreq, count[num])
20            if maxFreq == 1 or (maxFreq * freq[maxFreq] == i) or ((maxFreq-1)*(freq[maxFreq-1] + 1) == i):
21                ans = i + 1
22        return ans

C++ Solution

1
2cpp
3class Solution {
4public:
5    int maxEqualFreq(vector<int>& nums) {
6        int ans = 0;
7        int maxFreq = 0;
8        unordered_map<int, int> count;
9        unordered_map<int, int> freq;
10        for (int i = 0; i < nums.size(); ++i) {
11            int num = nums[i];
12            if (count.find(num) != count.end()){
13                freq[count[num]]--;
14                count[num]++;
15            }
16            else {
17                count[num] = 1;
18            }
19            freq[count[num]]++;
20            maxFreq = max(maxFreq, count[num]);
21            if ((maxFreq == 1 || maxFreq * freq[maxFreq] == i ||
22                 (maxFreq - 1) * (freq[maxFreq - 1] + 1) == i)) {
23                ans = i + 1;
24            }
25        }
26        return ans;
27    }
28};

Java Solution

1
2java
3class Solution {
4    public int maxEqualFreq(int[] nums) {
5        int ans = 0;
6        int maxFreq = 0;
7        Map<Integer, Integer> count = new HashMap<>();
8        Map<Integer, Integer> freq = new HashMap<>();
9        for (int i = 0; i < nums.length; ++i) {
10            int num = nums[i];
11            count.put(num, count.getOrDefault(num, 0) + 1);
12            freq.put(count.get(num)-1, freq.getOrDefault(count.get(num)-1, 0) - 1);
13            freq.put(count.get(num), freq.getOrDefault(count.get(num), 0) + 1);
14            maxFreq = Math.max(maxFreq, count.get(num));
15            if (maxFreq == 1 || maxFreq * freq.get(maxFreq) == i || (maxFreq - 1) * (freq.get(maxFreq - 1) + 1) == i) {
16                ans = i + 1;
17            }
18        }
19        return ans;
20    }
21}

JavaScript Solution

1
2javascript
3var maxEqualFreq = function(nums) {
4    let ans = 0;
5    let maxFreq = 0;
6    let count = {};
7    let freq = {};
8    for (let i = 0; i < nums.length; ++i) {
9        let num = nums[i];
10        if (count[num]) {
11            freq[count[num]]--;
12            count[num]++;
13        } else {
14            count[num] = 1;
15        }
16        if (freq[count[num]]) {
17            freq[count[num]]++;
18        } else {
19            freq[count[num]] = 1;
20        }
21        maxFreq = Math.max(maxFreq, count[num]);
22        if (maxFreq == 1 || maxFreq * freq[maxFreq] == i ||(maxFreq - 1) * (freq[maxFreq - 1] + 1) == i) {
23            ans = i + 1;
24        }
25    }
26    return ans;
27};

C# Solution

1
2csharp
3public class Solution {
4    public int MaxEqualFreq(int[] nums) {
5        int ans = 0;
6        int maxFreq = 0;
7        Dictionary<int, int> count = new Dictionary<int, int>();
8        Dictionary<int, int> freq = new Dictionary<int, int>();
9        for(int i = 0; i < nums.Length; ++i){
10            int num = nums[i];
11            if (count.ContainsKey(num)){
12                freq[count[num]]--;
13                count[num]++;
14            }
15            else{
16                count[num] = 1;
17            }
18            if (freq.ContainsKey(count[num])){
19                freq[count[num]]++;
20            }
21            else{
22                freq[count[num]] = 1;
23            }
24            maxFreq = Math.Max(maxFreq, count[num]);
25            if (maxFreq == 1 || maxFreq * freq[maxFreq] == i || (maxFreq - 1) * (freq.ContainsKey(maxFreq - 1) ? freq[maxFreq - 1] + 1 : 1) == i){
26                ans = i + 1;
27            }
28        }
29        return ans;
30    }
31}

Final Words

The solution involves regular usage of hash maps and iteration through the array. Note how the current prefix length (i+1) is stored in 'ans' whenever a valid prefix is found. This solution will have a time complexity of O(n), where 'n' is the size of the input array, and it will also have a space complexity of O(n), due to the additional storage used by the count and freq maps.The Python, JS, and Java solutions have been provided in the article. The solutions implement the approach described - they create a count of elements in the array and check for prefixes fulfilling the three conditions mentioned.

Upon assessing the complexity of their algorithms, we can conclude that their solutions are well-composed. To optimize the algorithm, perhaps, it would be best to consider the sequence of items counted and how frequently they occur. Another possible approach could be to remove unnecessary operations in the loop or reduce operations by determining the prefix length early.

For instance, to further enhance the efficiency of the JS solution, consider applying memoization. It should reduce repeated calculations. Moreover, you could use TypeScript's static typing to improve the development process, making variable types explicit.

Last but not least, for testing the solutions, you should choose well-balanced test inputs, including edge cases such as an empty array, an array of all equal elements and normal cases with elements in a random order.

These comprehensive solutions are a testimony to the usefulness of data structures like maps or dictionaries, highlighting their importance in problem-solving tasks like these. Because of their ability to track elements and their frequencies in a collection, hash maps make it much simpler to identify, retrieve, and manipulate significant statistics from raw data. Regardless of the language you're working in, mastering the use of appropriate data structures is crucial for becoming a versatile and efficient programmer.


Got a question? Ask the Teaching Assistant anything you don't understand.

Still not clear? Ask in the Forum,  Discord or Submit the part you don't understand to our editors.


TA 👨‍🏫