Katkat

Track the User with the Most Distinct Contacts: O(1) Query and Top-K for Changing k

GoogleSoftware Engineer · Technical Screen

Last updated

Design a class that records undirected messages between users and returns the user who has talked with the most distinct people, then make that query constant time and add a top-K query whose k changes on every call. It tests deduplication with hash sets, space analysis of per-user sets, and incremental ranking structures.

Users send each other messages. Each message is between two users, and messages are undirected: a message between A and B counts as contact for both A and B. Design a class that supports two operations:

  • register(a, b) records a message between users a and b.
  • most_active_user() returns the user who has exchanged messages with the largest number of distinct users.

The problem builds up over three parts. Throughout, analyze time and space complexity carefully, including the memory held by every per-user collection.

Clarifying Questions

  • Does messaging the same person many times count once or many times toward a user's activity?
  • If several users tie for the most distinct contacts, which one should be returned?
  • What should most_active_user() return before any message has been registered?
  • Can a user message themselves, and does that count?
  • Which is more frequent, register or the queries? Can messages ever be removed?

Part 1 — Register messages and find the most active user

Implement register(a, b) and most_active_user(). Then state the time complexity of each operation and the space complexity of the whole structure.

What This Part Should Cover

  • Correct deduplication of repeat contacts, applied to both users in the message
  • Time complexity per operation
  • A space bound that accounts for every stored contact, including the worst case where many pairs of users have talked

Part 2 — Make the query constant time

Optimize most_active_user() to run in O(1) while keeping register efficient.

What This Part Should Cover

  • An invariant that keeps the answer current after every register
  • An argument for why the invariant holds, including ties
  • The cost this moves into register

Part 3 — Top K with a changing k

Add top_k(k), which returns the k users with the most distinct contacts, ordered from most to fewest. The value of k can differ on every call, so a heap tied to one fixed k is not enough. Sorting all users on every call is the baseline to beat.

Clarifying Questions for this Part

  • If k is larger than the number of users, should the result contain every user, or is that an error?
  • Must users with equal counts appear in a fixed order?

What This Part Should Cover

  • The cost of the baselines: sorting, or building a size-k heap on every call
  • A structure whose query cost depends on k rather than on the number of users
  • The update cost in register and the extra space used
  • A tie rule that makes the output deterministic

Hints

Hint 1

Count what is distinct: A second message between the same two users must not raise either user's count, so decide what you must remember about each user to recognize a repeat contact.

Hint 2

What can a single message change?: Look at how much one call to `register` can change the counts, and whose standing it can affect.

Hint 3

Exploit how counts move: Counts only ever grow, and each change is small. Ask what kind of structure could stay in order cheaply under exactly that kind of change.

Follow-up questions

  • Messages can now be deleted, so counts can go down. Which of your structures still work, and what has to change?
  • The message volume no longer fits on one machine. How would you compute the global most active user and top K across shards?
  • Storing every contact set is too expensive. What would you give up to estimate distinct-contact counts in far less memory?