Determine whether two nodes are related
Company: Google
Role: Software Engineer
Category: Coding & Algorithms
Difficulty: medium
Interview Round: Technical Screen
Quick Answer: This question evaluates understanding of graph representations, reachability and connectivity concepts for ancestry relationships, including handling disconnected components and potential cycles.
Constraints
- 0 <= len(relations) <= 10^5
- Each relation is a pair (p, c) of integer chicken IDs.
- Either a or b may be absent from relations; if so the answer is False unless a == b.
- Input may contain cycles (invalid genealogy); the solution must terminate.
- Treat parent-child edges as undirected when forming family components.
Examples
Input: ([(1, 2), (1, 3), (3, 4), (10, 11)], 2, 4)
Expected Output: True
Explanation: 2 and 4 both descend from 1, so they share a common ancestor and are in the same component.
Input: ([(1, 2), (1, 3), (3, 4), (10, 11)], 2, 11)
Expected Output: False
Explanation: 2 is in the {1,2,3,4} family while 11 is in the separate {10,11} family — different components.
Input: ([(1, 2), (1, 3), (3, 4), (10, 11)], 3, 4)
Expected Output: True
Explanation: 3 is the direct parent of 4 (ancestor/descendant), so they are blood-related.
Input: ([(1, 2), (1, 3), (3, 4), (10, 11)], 5, 6)
Expected Output: False
Explanation: Neither 5 nor 6 appears in any relation, so neither belongs to a known family component.
Input: ([], 1, 2)
Expected Output: False
Explanation: With no relations, no chicken has a recorded family; 1 and 2 are unrelated.
Input: ([(1, 2)], 1, 1)
Expected Output: True
Explanation: A chicken is trivially related to itself (a == b) regardless of the relations.
Input: ([(1, 2), (2, 1)], 1, 2)
Expected Output: True
Explanation: Cyclic/invalid input: edges (1,2) and (2,1) still place 1 and 2 in one component, and the union-find loop terminates without hanging.
Input: ([(1, 2), (3, 4), (5, 6)], 2, 6)
Expected Output: False
Explanation: Three disjoint families; 2 is in {1,2} and 6 is in {5,6}, so they are not related.
Input: ([(1, 2), (2, 3), (4, 3)], 1, 4)
Expected Output: True
Explanation: Node 3 has two parents (2 and 4), linking the 1->2->3 lineage with 4, so 1 and 4 end up in the same component.
Input: ([(-1, -2), (-2, -3)], -1, -3)
Expected Output: True
Explanation: Negative IDs are handled the same way; -1 is an ancestor of -3 through -2.
Hints
- Direction does not matter for 'same lineage' — model each (parent, child) pair as an undirected edge and ask whether a and b land in the same connected component.
- Union-Find (disjoint set union) handles this cleanly: union every pair, then check whether find(a) == find(b). Path compression keeps it near-linear and avoids the infinite-loop risk of naive graph traversal on cyclic input.
- Handle the edge cases explicitly: a == b is trivially True; if a or b never appears in any relation, return False (an isolated chicken shares no ancestry).