Design structure for first unique login user
Company: Oracle
Role: Software Engineer
Category: Coding & Algorithms
Difficulty: easy
Interview Round: Technical Screen
Quick Answer: This question evaluates a candidate's ability to design an efficient online data structure that maintains a stream of login events and supports constant-time updates and queries for the earliest user with exactly one login.
Constraints
- Each operation is either login(userId) or firstUser().
- login and firstUser must each run in O(1) worst-case time.
- userId may be an integer or a string; the number of distinct users fits in memory.
- firstUser() returns a sentinel (None / null / -1 / empty) when no user has logged in exactly once.
Examples
Input: ([['login', 'A'], ['first', None], ['login', 'B'], ['first', None], ['login', 'A'], ['first', None], ['login', 'C'], ['first', None]],)
Expected Output: ['A', 'A', 'B', 'B']
Explanation: The worked example. After A logs in twice it stops being unique, so B (next-earliest unique) becomes the answer; C arriving later does not change the head.
Input: ([['first', None]],)
Expected Output: [None]
Explanation: No logins yet, so there is no user with exactly one login; firstUser() returns None.
Input: ([['login', 'X'], ['login', 'X'], ['first', None]],)
Expected Output: [None]
Explanation: X logs in twice, so it is no longer unique and there are no other users; firstUser() returns None.
Input: ([['login', 1], ['login', 2], ['login', 3], ['login', 1], ['login', 2], ['first', None]],)
Expected Output: [3]
Explanation: Users 1 and 2 each log in twice and drop out; only 3 remains unique, so it is the earliest (and only) qualifying user.
Input: ([['login', 'a'], ['login', 'b'], ['login', 'a'], ['login', 'b'], ['first', None], ['login', 'a'], ['first', None]],)
Expected Output: [None, None]
Explanation: Both a and b reach count 2 and leave the list. A third login of a (count 3) is a no-op, so no unique user ever returns.
Input: ([['login', 5], ['login', 5], ['login', 5], ['first', None], ['login', 7], ['first', None]],)
Expected Output: [None, 7]
Explanation: 5 reaches count 3 (never re-added once removed at count 2). After 7 logs in once it becomes the earliest unique user.
Input: ([['login', 'p'], ['login', 'q'], ['login', 'r'], ['login', 'q'], ['first', None], ['login', 'p'], ['first', None], ['login', 'r'], ['first', None]],)
Expected Output: ['p', 'r', None]
Explanation: q drops out first leaving p, q, r order -> p; then p's second login drops it leaving r; then r's second login empties the list -> None.
Hints
- A user qualifies only while its login count is exactly 1, and 'earliest' means by first-login time — so you need an order-preserving collection of the currently-unique users.
- A doubly linked list keeps unique users in arrival order and lets you delete an arbitrary user in O(1) when their count rises to 2 — store a hash map from userId to its node so you can find and unlink it instantly.
- Track each user's login count in a hash map. On the first login, append a node at the tail; on the second login, unlink that node; firstUser() just reads the head node. Once a count reaches 3+, no further list work is needed.