title: Unique Number of Occurrences
slug: unique-number-of-occurrences
difficulty: easy
leetcode_id: 1207
leetcode_url: https://leetcode.com/problems/unique-number-of-occurrences/
categories:
  - arrays
  - hash-tables
patterns:
  - prefix-sum

description: |
  Given an array of integers `arr`, return `true` *if the number of occurrences of each value in the array is **unique***, or `false` *otherwise*.

  In other words, no two different values in the array should appear the same number of times.

constraints: |
  - `1 <= arr.length <= 1000`
  - `-1000 <= arr[i] <= 1000`

examples:
  - input: "arr = [1,2,2,1,1,3]"
    output: "true"
    explanation: "The value 1 has 3 occurrences, 2 has 2 occurrences, and 3 has 1 occurrence. No two values have the same number of occurrences."
  - input: "arr = [1,2]"
    output: "false"
    explanation: "Both 1 and 2 appear exactly once, so their occurrence counts are not unique."
  - input: "arr = [-3,0,1,-3,1,1,1,-3,10,0]"
    output: "true"
    explanation: "The value 1 appears 4 times, -3 appears 3 times, 0 appears 2 times, and 10 appears 1 time. All counts are unique."

explanation:
  intuition: |
    This problem asks us to verify a **uniqueness property about frequencies**, not about the values themselves.

    Think of it like this: imagine you're organising a party and counting how many guests ordered each dish. You want to check if every dish has a *different* number of orders — no two dishes should be equally popular.

    The key insight is that this is a **two-step counting problem**:

    1. First, count how many times each value appears (value → frequency)
    2. Then, check if all those frequencies are unique (no duplicates in the frequency list)

    A hash map is perfect for the first step (counting), and a set is perfect for the second step (checking uniqueness). Since sets automatically reject duplicates, we can simply compare the number of unique frequencies to the total number of frequencies.

  approach: |
    We solve this using a **Hash Map + Set Approach**:

    **Step 1: Count occurrences of each value**

    - Create a hash map (dictionary) to store each value and its count
    - Iterate through the array, incrementing the count for each value
    - After this step, we have a mapping like `{1: 3, 2: 2, 3: 1}` for the first example

    &nbsp;

    **Step 2: Extract the frequency counts**

    - Get all the count values from the hash map
    - For the first example, this gives us `[3, 2, 1]`

    &nbsp;

    **Step 3: Check if all frequencies are unique**

    - Convert the list of frequencies to a set (which removes duplicates)
    - Compare the size of the set to the size of the original frequency list
    - If they're equal, all frequencies were unique; if the set is smaller, there were duplicates

    &nbsp;

    **Step 4: Return the result**

    - Return `True` if the lengths match, `False` otherwise

  common_pitfalls:
    - title: Confusing Value Uniqueness with Frequency Uniqueness
      description: |
        The problem asks if the **occurrence counts** are unique, not if the values themselves are unique.

        For example, `[1, 2]` has all unique values, but both values appear exactly once. Since the frequencies `[1, 1]` are not unique, the answer is `false`.

        Make sure you're checking uniqueness of the *counts*, not the original array elements.
      wrong_approach: "Checking if array values are unique"
      correct_approach: "Counting frequencies, then checking if those frequencies are unique"

    - title: Using a List Instead of a Set
      description: |
        Some solutions try to check for duplicate frequencies by iterating through a list and comparing elements. This works but is unnecessarily complex.

        Using a set is the idiomatic way to check for uniqueness — just compare `len(frequencies)` with `len(set(frequencies))`.
      wrong_approach: "Nested loops to find duplicate frequencies"
      correct_approach: "Convert to set and compare lengths"

    - title: Forgetting Edge Cases
      description: |
        Consider edge cases:
        - Single element array: `[5]` → frequency is `{5: 1}` → only one count → return `true`
        - All same elements: `[2, 2, 2]` → frequency is `{2: 3}` → only one count → return `true`

        Both cases have trivially unique frequencies since there's only one distinct value.

  key_takeaways:
    - "**Two-phase counting pattern**: Many problems require counting elements first, then analysing the counts themselves"
    - "**Set for uniqueness**: Converting a collection to a set and comparing lengths is the standard way to check for duplicates"
    - "**Hash map for frequency**: `Counter` or dictionary counting is a fundamental pattern for frequency-based problems"
    - "**Problem decomposition**: Breaking 'unique occurrences' into 'count frequencies' + 'check uniqueness' makes the solution clear"

  time_complexity: "O(n). We iterate through the array once to count frequencies, then iterate through the frequency values (at most n unique values) to build the set."
  space_complexity: "O(n). In the worst case, all elements are unique, so the hash map stores n key-value pairs, and the set stores n frequency values."

solutions:
  - approach_name: Hash Map and Set
    is_optimal: true
    code: |
      from collections import Counter

      def unique_occurrences(arr: list[int]) -> bool:
          # Step 1: Count occurrences of each value
          frequency = Counter(arr)

          # Step 2: Get all the count values
          counts = frequency.values()

          # Step 3: Check if all counts are unique
          # If set size equals list size, no duplicates exist
          return len(counts) == len(set(counts))
    explanation: |
      **Time Complexity:** O(n) — One pass to count, one pass to build the set.

      **Space Complexity:** O(n) — Hash map and set each store at most n elements.

      We use Python's `Counter` for concise frequency counting, then leverage the set's uniqueness property. If converting counts to a set reduces the size, there were duplicate frequencies.

  - approach_name: Manual Dictionary Counting
    is_optimal: true
    code: |
      def unique_occurrences(arr: list[int]) -> bool:
          # Count occurrences using a dictionary
          frequency = {}
          for num in arr:
              # Increment count, defaulting to 0 if not seen
              frequency[num] = frequency.get(num, 0) + 1

          # Get the frequency counts
          counts = list(frequency.values())

          # Check uniqueness: set removes duplicates
          return len(counts) == len(set(counts))
    explanation: |
      **Time Complexity:** O(n) — Same as the Counter approach.

      **Space Complexity:** O(n) — Same space usage.

      This approach shows the manual counting logic without relying on `Counter`. It's useful for understanding what happens under the hood or when `Counter` isn't available.

  - approach_name: Sorting Approach
    is_optimal: false
    code: |
      def unique_occurrences(arr: list[int]) -> bool:
          # Count occurrences
          frequency = {}
          for num in arr:
              frequency[num] = frequency.get(num, 0) + 1

          # Sort the counts
          counts = sorted(frequency.values())

          # Check adjacent elements for duplicates
          for i in range(1, len(counts)):
              if counts[i] == counts[i - 1]:
                  return False

          return True
    explanation: |
      **Time Complexity:** O(n log n) — Due to sorting the frequency counts.

      **Space Complexity:** O(n) — Storing frequencies and sorted counts.

      This approach sorts the counts and checks for adjacent duplicates. While correct, sorting adds unnecessary overhead compared to using a set. Included to show an alternative that doesn't use sets.
