You are given an online stream of user login events. Each event is a user ID (e.g., an integer or string) indicating that this user has just logged in.
You need to design a data structure that supports two operations:
-
login(userId)
Record that
userId
has logged in
one more time
.
-
firstUser()
Return the ID of the user who
-
has logged in
exactly once
so far, and
-
among all such users, is the
earliest
by login time (i.e., the one whose first login happened earliest in the stream),
or return a special value (e.g.,
null
or
-1
) if no such user exists.
Requirements:
-
Both
login
and
firstUser
must run in
O(1) worst-case time per operation
.
-
You can assume the number of distinct users fits in memory.
-
You may choose any reasonable representation for
userId
(e.g., integer or string), but specify your assumptions.
Example:
Suppose the login stream is:
-
login(A)
// A has logged in once
-
login(B)
// B has logged in once; A is still first unique
-
login(A)
// A now has logged in twice
-
login(C)
// C has logged in once
Then the sequence of firstUser() calls should return:
-
After
login(A)
:
firstUser()
→
A
-
After
login(B)
:
firstUser()
→
A
(A and B are unique, A is earlier)
-
After
login(A)
:
firstUser()
→
B
(A is no longer unique)
-
After
login(C)
:
firstUser()
→
B
(B is still the earliest unique user)
Design the data structure and its operations to meet the time complexity guarantees. You do not need to handle persistence or multi-threading; focus on the core algorithm and data organization.