Implement Union-Find and track components
Company: Motive
Role: Software Engineer
Category: Coding & Algorithms
Difficulty: medium
Interview Round: Onsite
Quick Answer: This question evaluates understanding and implementation of the Disjoint Set Union (Union-Find) data structure, covering union by rank, path compression, API design for union(a, b), connected(a, b), count(), and analysis of time and space complexity.
Constraints
- 1 <= n <= 10^5
- 0 <= a, b < n
- 0 <= number of operations <= 10^5
- Each operation type is either "union" or "connected"
- Self-operations are allowed (e.g. connected(x, x) is always true; union(x, x) is a no-op that leaves the count unchanged)
Examples
Input: (5, [["union", 0, 1], ["union", 2, 3], ["connected", 0, 1], ["connected", 0, 2], ["union", 1, 3], ["connected", 0, 3], ["connected", 0, 4]])
Expected Output: [4, 3, true, false, 2, true, false]
Explanation: Union 0-1 -> 4 comps; union 2-3 -> 3 comps; 0~1 true; 0~2 false; union 1-3 merges {0,1} and {2,3} -> 2 comps; 0~3 now true; node 4 isolated so 0~4 false.
Input: (1, [["connected", 0, 0]])
Expected Output: [true]
Explanation: A single node is always connected to itself.
Hints
- Maintain a parent array where parent[i] points toward the representative (root) of i's set. Initialize parent[i] = i so every node starts in its own component.
- find(x) walks to the root. Apply path compression by repointing every node on the path directly to the root, which flattens the tree for future queries.
- Use union by rank: attach the shorter tree under the taller one, and only increment rank when two equal-rank roots merge. Decrement the component count only when the two roots actually differ.
- connected(a, b) is simply find(a) == find(b). For thread-safety in a concurrent setting you'd guard find/union with a lock or use a lock-free CAS-based union, since path compression mutates shared state.