Unique Number of Occurrences
EasyPrompt
Given an array of integers arr, return true if the number of occurrences of each value in the array is unique or false otherwise.
Example 1:
Input: arr = [1,2,2,1,1,3]
Output: true
Explanation: The value 1 has 3 occurrences, 2 has 2 and 3 has 1. No two values have the same number of occurrences.Example 2:
Input: arr = [1,2]
Output: falseExample 3:
Input: arr = [-3,0,1,-3,1,1,1,-3,10,0]
Output: true
Constraints:
1 <= arr.length <= 1000-1000 <= arr[i] <= 1000
Approaches
3 approaches with complexity analysis and trade-offs.
This approach relies on sorting to solve the problem. First, the input array is sorted, which conveniently groups all identical numbers next to each other. Then, a single pass through the sorted array is enough to count the occurrences of each unique number. These counts are stored in a separate list. To check if the counts themselves are unique, this list of counts is also sorted. A final pass over the sorted counts list can easily reveal any duplicates by checking adjacent elements. If any duplicates are found, the function returns false; otherwise, it returns true.
Algorithm
- Sort the input array
arrto group identical elements together. - Initialize an empty list,
counts, to store the frequency of each unique number. - Iterate through the sorted array. For each unique number, count its occurrences and add the count to the
countslist. - After populating the
countslist, sort it as well. - Iterate through the sorted
countslist. If any two adjacent elements are identical, it means a frequency is not unique, so returnfalse. - If the entire
countslist is traversed without finding duplicates, returntrue.
Walkthrough
The core idea is to transform the problem of checking unique occurrences into a simpler problem of checking for duplicates in a sorted list. By sorting the initial array, we can easily compute the frequencies. By sorting the list of frequencies, we can easily check for uniqueness.
Algorithm Steps:
- Sort the input array
arr. - Initialize an
ArrayList<Integer>namedcounts. - Iterate through the sorted
arrusing an indexi. - Inside the loop, count the occurrences of the current element
arr[i]by checking the subsequent elements. - Add the final count to the
countslist. - Advance the index
ipast the block of identical elements. - After the loop, sort the
countslist. - Iterate through the sorted
countslist from the first element to the second-to-last element. - In each iteration, compare
counts.get(j)withcounts.get(j + 1). If they are equal, returnfalse. - If the loop completes, it means no two frequencies were the same, so return
true.
import java.util.ArrayList;import java.util.Arrays;import java.util.Collections;import java.util.List; class Solution { public boolean uniqueOccurrences(int[] arr) { Arrays.sort(arr); List<Integer> counts = new ArrayList<>(); int i = 0; while (i < arr.length) { int count = 1; while (i + 1 < arr.length && arr[i] == arr[i + 1]) { count++; i++; } counts.add(count); i++; } Collections.sort(counts); for (int j = 0; j < counts.size() - 1; j++) { if (counts.get(j).equals(counts.get(j + 1))) { return false; } } return true; }}Complexity
Time
O(N log N), where N is the number of elements in the array. The initial sort of the array dominates the time complexity. Counting frequencies takes O(N), and sorting the frequencies takes O(K log K), where K <= N.
Space
O(K), where K is the number of unique elements in `arr`. In the worst case (all elements are unique), the space complexity is O(N) to store the counts. Sorting algorithms might also use O(log N) to O(N) auxiliary space depending on the implementation.
Trade-offs
Pros
The logic is straightforward and relies on the fundamental concept of sorting.
It does not require complex data structures like hash maps.
Cons
The time complexity of O(N log N) is suboptimal compared to other approaches.
It requires modifying the input array by sorting it, or using extra space to store a sorted copy.
It involves multiple traversals and sorting operations, making it more complex to implement correctly.
A more efficient and standard approach for frequency-related problems is to use a hash map. This method involves two main steps. First, iterate through the input array and use a hash map to count the occurrences of each number. The number itself is the key, and its frequency is the value. Second, after all frequencies are counted, check if these frequencies are unique. This can be done efficiently by inserting all the frequency counts into a hash set and comparing the size of the hash set with the original number of unique elements (the size of the hash map).
Algorithm
- Create a
HashMap<Integer, Integer>to store the frequency of each number. - Iterate through the input array
arrand populate the hash map. For each number, increment its corresponding count. - After counting, the goal is to check if the frequencies (the values in the map) are unique.
- Create a
HashSet<Integer>from the values of the hash map. - The number of unique frequencies is the size of the hash set. The number of distinct elements is the size of the hash map.
- If these two sizes are equal, it means every element had a unique number of occurrences. Return
true, otherwise returnfalse.
Walkthrough
This approach decouples the counting from the uniqueness check, leading to a more efficient linear time solution.
Algorithm Steps:
- Initialize a
HashMap<Integer, Integer>calledfreqMap. - Iterate through each number
numin the input arrayarr. - For each
num, update its frequency infreqMap. You can usefreqMap.put(num, freqMap.getOrDefault(num, 0) + 1). - After the loop,
freqMapcontains all unique numbers and their corresponding frequencies. - To check if the frequencies are unique, we can get the collection of values from the map.
- Create a
HashSet<Integer>using the values fromfreqMap. A set only stores unique elements, so any duplicate frequencies will be discarded. - Compare the size of the
freqMap(which is the number of unique elements) with the size of thefreqSet(which is the number of unique frequencies). - If the sizes are equal, it means each unique element had a unique frequency. Return
true. - Otherwise, return
false.
import java.util.HashMap;import java.util.HashSet;import java.util.Map;import java.util.Set; class Solution { public boolean uniqueOccurrences(int[] arr) { // Step 1: Count frequencies of each number Map<Integer, Integer> freqMap = new HashMap<>(); for (int num : arr) { freqMap.put(num, freqMap.getOrDefault(num, 0) + 1); } // Step 2: Check if the frequencies are unique // We can do this by adding all frequencies to a set // and comparing the size of the set with the size of the map. Set<Integer> freqSet = new HashSet<>(freqMap.values()); return freqMap.size() == freqSet.size(); }}Complexity
Time
O(N), where N is the number of elements in the array. Populating the hash map requires a single pass through the array. Creating the hash set from the map's values takes O(K) time, where K is the number of unique elements (K <= N).
Space
O(K), where K is the number of unique elements in the array. The hash map will store K key-value pairs, and the hash set will store at most K frequencies. In the worst case, K=N.
Trade-offs
Pros
Optimal time complexity of O(N).
It is a general-purpose solution that works regardless of the range of numbers in the input array.
The code is often more concise and readable.
Cons
Requires extra space for both the hash map and the hash set.
Hashing can have a higher constant factor overhead compared to direct array indexing.
This approach is a highly optimized solution that takes advantage of the problem's constraints on the range of values in the input array. Instead of using a hash map, which involves computational overhead for hashing, we can use a simple array as a direct-access table (or frequency map). A second array is then used to check for the uniqueness of the frequencies found. This method avoids hashing and can be faster due to better cache performance and direct memory access.
Algorithm
- Given the constraint that numbers are in
[-1000, 1000], create an integer arrayfreqCountsof size 2001 to act as a frequency map. Use an offset of 1000 to map numbers to non-negative indices (e.g., number-1000maps to index0,0maps to1000,1000maps to2000). - Iterate through the input
arrand populatefreqCounts. For eachnum, incrementfreqCounts[num + 1000]. - The maximum possible frequency is
arr.length, which is at most 1000. Create a boolean arrayseenFreqsof size 1001 to track which frequencies have been seen. - Iterate through the
freqCountsarray. - For each
countinfreqCountsthat is greater than 0 (meaning it's an actual frequency of a number fromarr):- If
seenFreqs[count]is alreadytrue, it means this frequency has appeared before. Returnfalse. - Otherwise, mark this frequency as seen by setting
seenFreqs[count]totrue.
- If
- If the loop completes without returning, all frequencies were unique. Return
true.
Walkthrough
By using arrays as direct-access tables, we can achieve a very fast, constant-time lookup for both counting frequencies and checking for their uniqueness.
Algorithm Steps:
- Define an offset,
OFFSET = 1000, to handle negative numbers. - Create an integer array
freqCountsof size2 * OFFSET + 1(i.e., 2001), initialized to all zeros. - Iterate through each number
numin the inputarr. For eachnum, increment the count at the mapped index:freqCounts[num + OFFSET]++. - Create a boolean array
seenFreqsof sizearr.length + 1(or 1001, based on constraints), initialized to allfalse. This array will track if a frequency has been encountered before. - Iterate through the
freqCountsarray. - For each
countinfreqCounts: - If the
countis 0, continue to the next one. - If
seenFreqs[count]istrue, it means we have already seen another number with the same frequency. Returnfalse. - If
seenFreqs[count]isfalse, set it totrueto mark this frequency as seen. - If the loop finishes without finding any duplicate frequencies, return
true.
class Solution { public boolean uniqueOccurrences(int[] arr) { // Constraint: -1000 <= arr[i] <= 1000 // We can use an array as a frequency map. // Map values from -1000..1000 to indices 0..2000 int[] freqCounts = new int[2001]; int offset = 1000; for (int num : arr) { freqCounts[num + offset]++; } // Constraint: 1 <= arr.length <= 1000 // The maximum frequency can be 1000. // Use a boolean array to check for uniqueness of frequencies. boolean[] seenFreqs = new boolean[1001]; for (int count : freqCounts) { if (count > 0) { if (seenFreqs[count]) { // This frequency has been seen before. return false; } seenFreqs[count] = true; } } return true; }}Complexity
Time
O(N + M), where N is the length of the input array and M is the range of possible values (2001). We iterate through the input array once (O(N)) and the frequency array once (O(M)). Given the constraints, this is a very fast linear time solution.
Space
O(M + L), where M is the range of values (2001) and L is the maximum possible length of `arr` (1001). Since these are fixed by the problem constraints, the space complexity is effectively O(1).
Trade-offs
Pros
Extremely fast due to the use of arrays and direct indexing, avoiding hashing overhead.
The time complexity is linear, O(N + M), and the space complexity is constant with respect to the input size N, as it only depends on the problem's fixed constraints.
Cons
This solution is not general; it relies heavily on the specific constraints of the problem (
-1000 <= arr[i] <= 1000).It may use a large amount of memory if the range of possible values is large, even if the input array itself is small.
Solutions
Solution
class Solution {public boolean uniqueOccurrences(int[] arr) { Map<Integer, Integer> cnt = new HashMap<>(); for (int x : arr) { cnt.merge(x, 1, Integer : : sum); } return new HashSet<>(cnt.values()).size() == cnt.size(); }}class Solution {public: bool uniqueOccurrences(vector<int> &arr) { unordered_map<int, int> cnt; for (int &x : arr) { ++cnt[x]; } unordered_set<int> vis; for (auto &[_, v] : cnt) { if (vis.count(v)) { return false; } vis.insert(v); } return true; }};class Solution: def uniqueOccurrences(self, arr: List[int]) -> bool: cnt = Counter(arr) return len(set(cnt . values())) == len(cnt)Video walkthrough
Newsletter
One sharp idea, every week
System design and interview prep — short enough to finish.
No spam. Unsubscribe anytime.
Practice
Same difficulty — related problems to reinforce the pattern.