from collections import deque, Counter
class FirstUnique:
def __init__(self, nums):
self.queue = deque()
self.counts = Counter()
for num in nums:
self.add(num)
def showFirstUnique(self):
while self.queue and self.counts[self.queue[0]] > 1:
self.queue.popleft()
return self.queue[0] if self.queue else -1
def add(self, value):
self.counts[value] += 1
if self.counts[value] == 1:
self.queue.append(value)
class FirstUnique {
unordered_map<int, int> counts;
queue<int> q;
public:
FirstUnique(vector<int>& nums) {
for (int num : nums) {
add(num);
}
}
int showFirstUnique() {
while (!q.empty() && counts[q.front()] > 1) {
q.pop();
}
return q.empty() ? -1 : q.front();
}
void add(int value) {
counts[value]++;
if (counts[value] == 1) {
q.push(value);
}
}
};
import java.util.*;
class FirstUnique {
private Map<Integer, Integer> counts;
private Queue<Integer> queue;
public FirstUnique(int[] nums) {
counts = new HashMap<>();
queue = new LinkedList<>();
for (int num : nums) {
add(num);
}
}
public int showFirstUnique() {
while (!queue.isEmpty() && counts.get(queue.peek()) > 1) {
queue.poll();
}
return queue.isEmpty() ? -1 : queue.peek();
}
public void add(int value) {
counts.put(value, counts.getOrDefault(value, 0) + 1);
if (counts.get(value) == 1) {
queue.offer(value);
}
}
}
class FirstUnique {
constructor(nums) {
this.counts = new Map();
this.queue = [];
for (const num of nums) {
this.add(num);
}
}
showFirstUnique() {
while (this.queue.length > 0 && this.counts.get(this.queue[0]) > 1) {
this.queue.shift();
}
return this.queue.length === 0 ? -1 : this.queue[0];
}
add(value) {
this.counts.set(value, (this.counts.get(value) || 0) + 1);
if (this.counts.get(value) === 1) {
this.queue.push(value);
}
}
}
The First Unique Number problem asks you to design a class that takes a list of integers nums
and supports the following operations:
showFirstUnique()
: Returns the first unique integer in the current sequence (i.e., the first integer that occurs exactly once). If there is no such integer, return -1
.add(value)
: Adds the integer value
to the sequence.You are required to maintain the order of numbers as they are added, and efficiently determine the first unique number at any time. The input guarantees that each number is an integer, and you must not reuse or skip elements.
At first glance, you might think to check for uniqueness by scanning through the sequence each time showFirstUnique()
is called. However, this would be very inefficient, especially if the list is long or if there are many operations.
To optimize, we need a way to quickly:
This suggests using a hash map (or dictionary) for counting occurrences, and a queue to keep track of the insertion order of unique candidates.
The main trick is to only keep track of numbers that are still potentially unique in our queue, and skip over numbers that have become duplicates as we process queries.
To efficiently solve the problem, we use two main data structures:
Here is the step-by-step algorithm:
nums
, call add(num)
to process it.
value
in the hash map.value
, append it to the queue.-1
(no unique number). Otherwise, return the number at the front of the queue.This approach ensures that both adding numbers and finding the first unique number are efficient.
Let's walk through an example:
Input: nums = [2,3,5] Operations: showFirstUnique() -> returns 2 add(5) showFirstUnique() -> returns 2 add(2) showFirstUnique() -> returns 3 add(3) showFirstUnique() -> returns -1
showFirstUnique()
: 2 is at front and unique, so return 2.add(5)
: counts[5]=2, queue unchanged.showFirstUnique()
: 2 is still unique, return 2.add(2)
: counts[2]=2, queue unchanged.showFirstUnique()
: 2 is no longer unique, remove from queue. Next is 3, which is unique, so return 3.add(3)
: counts[3]=2.showFirstUnique()
: 3 is now not unique, remove 3. Next is 5, which is not unique, remove 5. Queue is empty, return -1.showFirstUnique()
, scan the entire list to find the first unique number. This is O(N) per query, where N is the list size.
add(value)
: O(1) time, as updating a hash map and appending to a queue are constant-time operations.
showFirstUnique()
: Each number is removed from the queue at most once, so across all operations, the total time spent is O(N), making the amortized time per call O(1).
The First Unique Number problem is elegantly solved by combining a hash map for counting occurrences and a queue for maintaining order. This design allows us to efficiently add numbers and retrieve the first unique number at any time, using only O(1) amortized time per operation. The key insight is to only keep track of candidates for uniqueness in the queue, and quickly skip over numbers that have become duplicates. This approach is both simple and highly effective for the problem's requirements.