First Unique Number

Updated on 29 May, 2025
First Unique Number header image

Problem Statement

Design a class FirstUnique that manages a queue of integers and supports three operations efficiently:

  • FirstUnique(int[] nums): Initializes the queue with an array of integers.
  • int showFirstUnique(): Returns the first unique integer in the queue. If no such integer exists, return -1.
  • void add(int value): Adds a new integer to the end of the queue.

The solution must ensure that operations are fast and scalable for large input sizes.

Examples

Example 1

Input:

["FirstUnique","showFirstUnique","add","showFirstUnique","add","showFirstUnique","add","showFirstUnique"]
[[[2,3,5]],[],[5],[],[2],[],[3],[]]

Output:

[null,2,null,2,null,3,null,-1]

Explanation:

FirstUnique firstUnique = new FirstUnique([2,3,5])
firstUnique.showFirstUnique() => 2
firstUnique.add(5)             => queue = [2,3,5,5]
firstUnique.showFirstUnique() => 2
firstUnique.add(2)             => queue = [2,3,5,5,2]
firstUnique.showFirstUnique() => 3
firstUnique.add(3)             => queue = [2,3,5,5,2,3]
firstUnique.showFirstUnique() => -1

Example 2

Input:

["FirstUnique","showFirstUnique","add","add","add","add","add","showFirstUnique"]
[[[7,7,7,7,7,7]],[],[7],[3],[3],[7],[17],[]]

Output:

[null,-1,null,null,null,null,null,17]

Example 3

Input:

["FirstUnique","showFirstUnique","add","showFirstUnique"]
[[[809]],[],[809],[]]

Output:

[null,809,null,-1]

Constraints

  • 1 <= nums.length <= 10^5
  • 1 <= nums[i], value <= 10^8
  • At most 50000 calls will be made to showFirstUnique and add.

Approach and Intuition

The challenge lies in dynamically identifying the first unique integer while supporting fast add and showFirstUnique operations. A naïve scan of the queue each time is inefficient. Instead, we use:

1. Hash Map (occurrences)

  • Tracks how many times each number has appeared.
  • Time complexity: O(1) for updates and lookups.

2. Queue or Linked Set (uniqueQueue)

  • Maintains the insertion order of unique integers.
  • Elements are only present if their count is exactly 1.
  • Time complexity: O(1) for peek, add, and remove.

3. Operations

  • Constructor (FirstUnique(nums)):

    • For each number:

      • Increment its count in occurrences.
      • If it's the first time, add to uniqueQueue.
      • If the count becomes >1, remove it from uniqueQueue if it exists.
  • showFirstUnique():

    • Return the front element of uniqueQueue if it exists.
    • If no unique element remains, return -1.
  • add(value):

    • Same logic as constructor for adding a new number and updating the unique tracker.

This approach ensures that operations run in constant time on average, which is critical under the given constraints.

Solutions

  • Java
  • Python
java
class UniqueTracker {

  private Set<Integer> queueSet = new LinkedHashSet<>();
  private Map<Integer, Boolean> mapUnique = new HashMap<>();
  
  public UniqueTracker(int[] elements) {
    for (int element : elements) {
      this.add(element);
    }
  }
    
  public int showFirstUnique() {
    if (!queueSet.isEmpty()) {
       return queueSet.iterator().next();
    }
    return -1;
  }
    
  public void add(int number) {
    if (!mapUnique.containsKey(number)) {
      mapUnique.put(number, true);
      queueSet.add(number);
    } else if (mapUnique.get(number)) {
      mapUnique.put(number, false);
      queueSet.remove(number);
    }
  }
}

The provided Java code is designed to manage and identify the first unique number in a stream of integers using a combination of a HashSet and a HashMap. The functionality is encapsulated within the UniqueTracker class, which performs the operations using a set (queueSet) to maintain insertion order and a map (mapUnique) to track the uniqueness of numbers.

  • The UniqueTracker constructor takes an array of integers and adds them to the structure by calling the add method for each element.
  • The showFirstUnique method checks if the queueSet is not empty and returns the first element, which is guaranteed to be the first unique number since it preserves insertion order. If the set is empty, it returns -1, indicating no unique number is present.
  • The add method processes a new number each time it is called:
    1. If the number is not already in mapUnique, it adds the number to both mapUnique (setting its value to true to denote it as unique) and queueSet.
    2. If the number is already in mapUnique and its value is true (indicating it was considered unique before), it sets its value to false (no longer unique) and removes it from queueSet.

Through these operations, the UniqueTracker efficiently tracks and updates the first unique number in real-time, providing an effective solution for problems that require such functionality.

python
from collections import OrderedDict

class UniqueNumberTracker:

    def __init__(self, numbers: List[int]):
        self._data = OrderedDict()
        self._unique_flags = {}
        for number in numbers:
            self.append(number)
        
    def findFirstUnique(self) -> int:
        if self._data:
            return next(iter(self._data))
        return -1
        
    def append(self, number: int) -> None:
        if number not in self._unique_flags:
            self._unique_flags[number] = True
            self._data[number] = None
        else:
            if self._unique_flags[number]:
                self._unique_flags[number] = False
                self._data.pop(number)

The Python program provided implements a class UniqueNumberTracker utilizing the OrderedDict from the collections module to manage and track unique numbers efficiently.

  • At initialization (__init__ method), the tracker accepts a list of integers. Each number is processed to determine if it is unique by adding it to an OrderedDict _data. This ensures that the insertion order is maintained, which is crucial for determining the first unique number.
  • The findFirstUnique method checks if the _data dictionary has entries and returns the first key (which represents the first unique number) if present. If the dictionary is empty, indicating no unique numbers, it returns -1.
  • The append method allows addition of new numbers to the tracker. It includes logic to:
    • Add the number as unique if it’s not already in the _unique_flags dictionary.
    • If already present and flagged as unique, the number is marked as not unique and removed from the _data dictionary to reflect this change.

This setup allows for efficient tracking and retrieval of the first unique number from a stream of data, by keeping up-to-date records of each number’s uniqueness status and order of occurrence.

Comments

No comments yet.