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.
Input: ([('A', 'B'), ('A', 'C'), ('B', 'D')], 'C')
Expected Output: ('ic', 0, 0)
Explanation: C has no direct reports and no descendants.
Input: ([], 'X')
Expected Output: None
Explanation: Edge case: the org chart is empty.
Input: ([('A', 'B'), ('X', 'Y')], 'X')
Expected Output: ('mgr', 1, 1)
Explanation: The data can be a forest, so X is a separate top-level manager.
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.
Input: ([], [])
Expected Output: []
Explanation: Edge case: no employees and no queries.
Input: ([], ['X'])
Expected Output: [None]
Explanation: Edge case: query on an empty chart.
Input: ([('A', 'B'), ('A', 'C'), ('B', 'D'), ('B', 'E'), ('E', 'F')], ['B', 'F', 'A'])
Expected Output: [('mgr', 2, 3), ('ic', 0, 0), ('mgr', 2, 5)]
Explanation: B has descendants D, E, and F. A has five total descendants.
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.
Input: ([('A', 'B'), ('X', 'Y')], [('lookUp', 'Z'), ('add', 'Y', 'Q'), ('lookUp', 'X'), ('lookUp', 'Y')])
Expected Output: [None, ('mgr', 1, 2), ('mgr', 1, 1)]
Explanation: Unknown names return None, and updates only affect the relevant branch.
Input: ([('A', 'B'), ('B', 'C')], [('lookUp', 'A'), ('add', 'D', 'A'), ('lookUp', 'D'), ('lookUp', 'A'), ('lookUp', 'C')])
Expected Output: [('mgr', 1, 2), ('mgr', 1, 3), ('mgr', 1, 2), ('ic', 0, 0)]
Explanation: An existing root subtree can be attached under a new manager.
Input: ([('R', 'S'), ('T', 'U'), ('U', 'V')], [('add', 'S', 'T'), ('lookUp', 'R'), ('lookUp', 'S'), ('lookUp', 'T')])
Expected Output: [('mgr', 1, 4), ('mgr', 1, 3), ('mgr', 1, 2)]
Explanation: Adding an existing root subtree updates all ancestors above the insertion point.
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.