Implement follow graph with snapshots and recommendations
Company: OpenAI
Role: Software Engineer
Category: Coding & Algorithms
Difficulty: medium
Interview Round: Technical Screen
Quick Answer: This question evaluates design and implementation skills for mutable graph data structures with point-in-time snapshotting and recommendation ranking, testing knowledge of data structures, versioning, graph traversal, and algorithmic time/space complexity analysis.
Part 1: Basic Follow/Unfollow Graph
Constraints
- 0 <= n <= 10^5
- 0 <= len(operations) <= 2 * 10^5
- All user ids in operations are integers in the range [0, n - 1]
- Average O(1) set operations are acceptable
Examples
Input: (4, [('follow', 0, 1), ('check', 0, 1), ('unfollow', 0, 1), ('check', 0, 1), ('follow', 0, 0), ('check', 0, 0)])
Expected Output: [True, False, False]
Explanation: User 0 first follows 1, then unfollows 1. Self-follow is ignored, so checking (0, 0) is False.
Input: (3, [('follow', 1, 2), ('follow', 1, 2), ('check', 1, 2), ('unfollow', 1, 2), ('unfollow', 1, 2), ('check', 1, 2)])
Expected Output: [True, False]
Explanation: Repeated follow and unfollow operations should not break the state.
Input: (2, [])
Expected Output: []
Explanation: Edge case: no operations means no check results.
Input: (5, [('follow', 0, 1), ('follow', 0, 2), ('follow', 2, 1), ('check', 0, 2), ('check', 2, 0), ('unfollow', 0, 2), ('check', 0, 2)])
Expected Output: [True, False, False]
Explanation: Checks should always reflect the current graph after each update.
Hints
- A hash set for each user is enough to represent who they currently follow.
- Make `follow` and `unfollow` idempotent so repeated operations do not cause errors.
Part 2: Follow Graph with Snapshots and Historical Queries
Constraints
- 0 <= n <= 10^5
- 0 <= len(operations) <= 2 * 10^5
- All user ids are in [0, n - 1]
- Each `check` uses a valid snapshot id that has already been created
Examples
Input: (3, [('follow', 0, 1), ('snapshot',), ('unfollow', 0, 1), ('snapshot',), ('check', 0, 0, 1), ('check', 1, 0, 1)])
Expected Output: [0, 1, True, False]
Explanation: At snapshot 0, 0 follows 1. After unfollow and snapshot 1, the relation no longer exists.
Input: (2, [('follow', 0, 1), ('unfollow', 0, 1), ('follow', 0, 1), ('snapshot',), ('check', 0, 0, 1)])
Expected Output: [0, True]
Explanation: Multiple changes before the same snapshot should collapse into the final state seen by that snapshot.
Input: (1, [])
Expected Output: []
Explanation: Edge case: no operations.
Input: (2, [('follow', 0, 0), ('snapshot',), ('check', 0, 0, 0)])
Expected Output: [0, False]
Explanation: Self-follow is ignored, so even the snapshot records no such relation.
Input: (3, [('follow', 1, 2), ('snapshot',), ('snapshot',), ('check', 1, 1, 2)])
Expected Output: [0, 1, True]
Explanation: If nothing changes between snapshots, the old state should still be visible in later snapshots.
Hints
- You do not need to copy the whole graph at every snapshot. Store only when a specific follow relation changes.
- For each pair `(a, b)`, keep a sorted history of `(snapshot_id, state)` and binary-search it during historical queries.
Part 3: Recommend Top-K Users by Two-Hop Follow Count
Constraints
- 0 <= n <= 10^5
- 0 <= len(operations) <= 2 * 10^5
- All user ids are in [0, n - 1]
- Self-follow should be ignored
- If fewer than k candidates have positive score, return all of them
Examples
Input: (5, [('follow', 0, 1), ('follow', 0, 2), ('follow', 1, 3), ('follow', 2, 3), ('follow', 2, 4), ('recommend', 0, 2)])
Expected Output: [[3, 4]]
Explanation: User 3 gets score 2 because both followed users 1 and 2 follow 3. User 4 gets score 1.
Input: (6, [('follow', 0, 1), ('follow', 0, 2), ('follow', 1, 4), ('follow', 2, 3), ('recommend', 0, 2)])
Expected Output: [[3, 4]]
Explanation: Users 3 and 4 both have score 1, so the smaller id 3 comes first.
Input: (5, [('follow', 0, 1), ('follow', 1, 2), ('follow', 1, 3), ('recommend', 0, 3), ('unfollow', 0, 1), ('recommend', 0, 3)])
Expected Output: [[2, 3], []]
Explanation: After unfollowing 1, user 0 has no two-hop recommendation source left.
Input: (3, [('recommend', 0, 2), ('follow', 0, 1), ('recommend', 0, 0)])
Expected Output: [[], []]
Explanation: Edge cases: no followees means no recommendations, and k = 0 should return an empty list.
Input: (3, [('follow', 1, 1), ('follow', 0, 1), ('recommend', 0, 2)])
Expected Output: [[]]
Explanation: Self-follow is ignored, so user 1 contributes no second-hop candidates.
Hints
- For a recommendation query on user `u`, iterate through everyone `u` follows, then through each of their follow sets, and count candidate frequencies.
- After counting, sort candidates by `(-score, user_id)` and take the first `k`.