Design org chart lookup with preprocessing
Company: Patreon
Role: Software Engineer
Category: Coding & Algorithms
Difficulty: easy
Interview Round: Technical Screen
Quick Answer: This question evaluates skills in data structures and algorithms, specifically tree/forest representation, aggregation of subtree metrics, dynamic updates, and rigorous time and space complexity analysis.
Part 1: Single org chart lookUp
Constraints
- 0 <= len(edges) <= 200000
- Names are unique non-empty strings
- The input graph is a forest: no cycles and each report has at most one manager
- All known people appear in at least one pair
Examples
Input: ([('A', 'B'), ('A', 'C'), ('B', 'D')], 'A')
Expected Output: ('mgr', 2, 3)
Explanation: A directly manages B and C, and indirectly manages D.
Input: ([('A', 'B'), ('A', 'C'), ('B', 'D')], 'B')
Expected Output: ('mgr', 1, 1)
Explanation: B directly manages D and has one total descendant.
Hints
- Build an adjacency list from manager to direct reports.
- For a single query, you only need to traverse the queried person's subtree to count all descendants.
Part 2: Preprocess org chart for O(1) lookUps
Constraints
- 0 <= len(edges) <= 200000
- 0 <= len(queries) <= 200000
- Names are unique non-empty strings
- The input graph is a forest: no cycles and each report has at most one manager
Examples
Input: ([('A', 'B'), ('A', 'C'), ('B', 'D')], ['A', 'B', 'C', 'D'])
Expected Output: [('mgr', 2, 3), ('mgr', 1, 1), ('ic', 0, 0), ('ic', 0, 0)]
Explanation: All query answers are returned after one preprocessing pass.
Input: ([('CEO1', 'E1'), ('CEO2', 'E2'), ('CEO1', 'M1'), ('M1', 'I1')], ['CEO2', 'M1', 'ZZ'])
Expected Output: [('mgr', 1, 1), ('mgr', 1, 1), None]
Explanation: The chart is a forest, and unknown names should return None.
Hints
- If lookups are frequent, compute the answer for every known employee once and store it in a hash map.
- A postorder traversal from each root lets you compute total descendant counts for all nodes.
Part 3: Org chart with add and live lookUps
Constraints
- 0 <= len(initial_edges) <= 200000
- 0 <= len(operations) <= 200000
- Names are unique non-empty strings
- The initial chart is a forest: no cycles and each report has at most one manager
- Each add operation is valid: no cycles, no duplicate edge, and report has no existing different manager
Examples
Input: ([('A', 'B'), ('A', 'C')], [('lookUp', 'A'), ('add', 'B', 'D'), ('lookUp', 'A'), ('lookUp', 'B'), ('lookUp', 'D')])
Expected Output: [('mgr', 2, 2), ('mgr', 2, 3), ('mgr', 1, 1), ('ic', 0, 0)]
Explanation: After adding D under B, A's total descendants increase from 2 to 3.
Input: ([], [('add', 'CEO', 'CTO'), ('lookUp', 'CEO'), ('lookUp', 'CTO')])
Expected Output: [('mgr', 1, 1), ('ic', 0, 0)]
Explanation: Edge case: start with an empty chart and create nodes through add.
Hints
- Store parent pointers as well as children lists.
- When adding a report under a manager, only that manager and its ancestors need their total descendant counts updated.