1376. Time Needed to Inform All Employees
Problem Description
You have a company with n
employees, each having a unique ID from 0
to n - 1
. The company has a hierarchical structure with one head (CEO) whose ID is headID
.
The company structure is represented by a manager
array where:
manager[i]
indicates the direct manager of employeei
manager[headID] = -1
(the head has no manager)- The organizational structure forms a tree
When urgent news needs to be spread throughout the company, it starts from the head and cascades down through the hierarchy. Each manager informs their direct subordinates, who then inform their subordinates, and so on.
The informTime
array indicates how long each employee takes to inform all their direct subordinates:
informTime[i]
= minutes needed for employeei
to inform all their direct subordinates- After
informTime[i]
minutes, all direct subordinates of employeei
can start spreading the news to their own subordinates
The goal is to find the total time needed for the news to reach every employee in the company.
For example, if the head takes 3 minutes to inform their subordinates, and one of those subordinates takes 5 minutes to inform their team, while another takes 2 minutes, the news would reach the last employee after max(3 + 5, 3 + 2) = 8
minutes. The function returns the maximum time along any path from the head to a leaf employee.
Flowchart Walkthrough
First, let's pin down the algorithm using the Flowchart. Here's a step-by-step walkthrough:
Is it a graph?
- Yes: The problem involves employees and their manager relationships, which forms a graph structure. Each employee is a node, and the manager-subordinate relationships are edges.
Is it a tree?
- Yes: The problem explicitly states "it is guaranteed that the subordination relationships have a tree structure." There's one root (the head with
headID
), and each employee has exactly one manager (except the head), forming a hierarchical tree structure.
DFS
- Since we identified this as a tree problem, the flowchart leads us directly to DFS (Depth-First Search).
Conclusion: The flowchart suggests using DFS for this problem.
This makes perfect sense because:
- We have a tree structure representing the company hierarchy
- We need to calculate the time for information to propagate from the root (head) to all leaves (employees with no subordinates)
- The total time for any branch is the sum of inform times along that path
- We need to find the maximum time among all paths from root to leaves
- DFS is ideal for traversing tree structures and calculating path-based metrics
The DFS approach allows us to recursively calculate the time needed for each employee to inform all their subordinates (direct and indirect), taking the maximum time among all branches at each level.
Intuition
Think about how information flows in a company hierarchy. When the CEO announces something, they tell their direct reports, who then tell their teams, and so on. The key insight is that this process happens in parallel - once a manager finishes informing their direct subordinates, those subordinates can all start informing their own teams simultaneously.
Consider a simple example: if the CEO takes 3 minutes to inform two VPs, and one VP takes 5 minutes to inform their team while the other takes 2 minutes, both VPs start their communication at the same time (after 3 minutes). The total time isn't 3 + 5 + 2 = 10
minutes, but rather 3 + max(5, 2) = 8
minutes because the VPs work in parallel.
This naturally leads to a recursive structure. For any employee, the total time to inform everyone below them in the hierarchy is:
- The time they need to inform their direct subordinates (
informTime[i]
) - Plus the maximum time any of their subordinates needs to inform their entire subtree
Why the maximum? Because all direct subordinates receive the information at the same time and start spreading it in parallel. We only care about the longest path since that determines when the last person gets informed.
The DFS approach emerges naturally from this observation. Starting from the head, we recursively calculate the time for each subtree. At each node, we add the current employee's inform time to the maximum time among all their subordinates' subtrees. This gives us the total time for that branch of the organization.
The base case is straightforward: employees with no subordinates (leaf nodes) contribute 0 additional time since they don't need to inform anyone else.
Learn more about Tree, Depth-First Search and Breadth-First Search patterns.
Solution Approach
The implementation follows the DFS pattern we identified, with a few key components:
1. Building the Adjacency List
First, we need to transform the manager
array into a more useful structure. The manager
array tells us who manages each employee, but for DFS, we need the opposite - who each employee manages.
g = defaultdict(list)
for i, x in enumerate(manager):
g[x].append(i)
This creates an adjacency list where g[i]
contains all direct subordinates of employee i
. For example, if manager = [2, 2, -1, 2]
, then g[2] = [0, 1, 3]
(employee 2 manages employees 0, 1, and 3).
2. DFS Function
The core logic is in the recursive dfs(i)
function, which calculates the time needed for employee i
to inform all their subordinates (direct and indirect):
def dfs(i: int) -> int:
ans = 0
for j in g[i]:
ans = max(ans, dfs(j) + informTime[i])
return ans
Here's how it works:
- For each direct subordinate
j
of employeei
, we recursively calculatedfs(j)
- the time for subordinatej
to inform their entire subtree - We add
informTime[i]
to this value because employeei
needs this time to inform subordinatej
beforej
can start spreading the news - We take the maximum among all subordinates because they work in parallel - the slowest branch determines the total time
- If an employee has no subordinates (leaf node), the loop doesn't execute and we return 0
3. Starting the DFS
The solution starts by calling dfs(headID)
, which calculates the time for the head to inform the entire company.
Time Complexity: O(n)
where n
is the number of employees. We visit each employee exactly once during the DFS traversal.
Space Complexity: O(n)
for the adjacency list storage and the recursion stack in the worst case (when the tree is skewed).
The elegance of this solution lies in how it naturally handles the parallel nature of information spreading through the max
operation, ensuring we track the critical path that determines when the last employee receives the news.
Ready to land your dream job?
Unlock your dream job with a 5-minute evaluator for a personalized learning plan!
Start EvaluatorExample Walkthrough
Let's walk through a small company example to illustrate the solution approach.
Input:
n = 6
employees (IDs: 0 to 5)headID = 2
(employee 2 is the CEO)manager = [2, 2, -1, 2, 3, 3]
- Employee 0's manager is 2
- Employee 1's manager is 2
- Employee 2 has no manager (CEO)
- Employee 3's manager is 2
- Employee 4's manager is 3
- Employee 5's manager is 3
informTime = [0, 0, 3, 2, 0, 0]
- Employee 2 takes 3 minutes to inform subordinates
- Employee 3 takes 2 minutes to inform subordinates
- Others take 0 minutes (they have no subordinates)
Step 1: Build the adjacency list
From the manager
array, we create:
g[2] = [0, 1, 3] // CEO manages employees 0, 1, and 3 g[3] = [4, 5] // Employee 3 manages employees 4 and 5
The organizational tree looks like:
2 (CEO) /|\ / | \ 0 1 3 / \ 4 5
Step 2: Execute DFS from the head
Starting with dfs(2)
:
- Employee 2 has subordinates [0, 1, 3]
- Calculate time for each branch:
dfs(0)
: Employee 0 has no subordinates → returns 0- Time through this branch:
0 + informTime[2] = 0 + 3 = 3
- Time through this branch:
dfs(1)
: Employee 1 has no subordinates → returns 0- Time through this branch:
0 + informTime[2] = 0 + 3 = 3
- Time through this branch:
dfs(3)
: Employee 3 has subordinates [4, 5]dfs(4)
: Employee 4 has no subordinates → returns 0- Time:
0 + informTime[3] = 0 + 2 = 2
- Time:
dfs(5)
: Employee 5 has no subordinates → returns 0- Time:
0 + informTime[3] = 0 + 2 = 2
- Time:
dfs(3)
returnsmax(2, 2) = 2
- Time through this branch:
2 + informTime[2] = 2 + 3 = 5
dfs(2)
returnsmax(3, 3, 5) = 5
Result: The total time needed is 5 minutes.
Timeline breakdown:
- Minute 0-3: CEO (employee 2) informs employees 0, 1, and 3
- Minute 3: Employees 0, 1, and 3 receive the news simultaneously
- Minute 3-5: Employee 3 informs employees 4 and 5
- Minute 5: Employees 4 and 5 receive the news (last to know)
The critical path is 2 → 3 → (4 or 5), taking 3 + 2 = 5 minutes total.
Solution Implementation
1class Solution:
2 def numOfMinutes(
3 self, n: int, headID: int, manager: List[int], informTime: List[int]
4 ) -> int:
5 """
6 Calculate the total time needed to inform all employees in a company hierarchy.
7
8 Args:
9 n: Total number of employees
10 headID: ID of the head of the company
11 manager: List where manager[i] is the direct manager of employee i
12 informTime: List where informTime[i] is the time needed for employee i to inform all direct subordinates
13
14 Returns:
15 The total minutes needed to inform all employees
16 """
17
18 def dfs(employee_id: int) -> int:
19 """
20 Perform depth-first search to find the maximum time needed to inform all subordinates.
21
22 Args:
23 employee_id: Current employee being processed
24
25 Returns:
26 Maximum time needed to inform all subordinates of this employee
27 """
28 max_time = 0
29
30 # Iterate through all direct subordinates of the current employee
31 for subordinate_id in adjacency_list[employee_id]:
32 # Recursively calculate time for each subordinate branch
33 # Add current employee's inform time to the maximum subordinate time
34 max_time = max(max_time, dfs(subordinate_id) + informTime[employee_id])
35
36 return max_time
37
38 # Build adjacency list representation of the company hierarchy
39 # Key: manager's ID, Value: list of direct subordinates' IDs
40 adjacency_list = defaultdict(list)
41
42 # Populate the adjacency list
43 for employee_id, manager_id in enumerate(manager):
44 adjacency_list[manager_id].append(employee_id)
45
46 # Start DFS from the head of the company
47 return dfs(headID)
48
1class Solution {
2 // Adjacency list to represent the tree structure (manager -> subordinates)
3 private List<Integer>[] adjacencyList;
4 // Array to store the time needed for each manager to inform their subordinates
5 private int[] informTime;
6
7 public int numOfMinutes(int n, int headID, int[] manager, int[] informTime) {
8 // Initialize adjacency list for n employees
9 adjacencyList = new List[n];
10 Arrays.setAll(adjacencyList, index -> new ArrayList<>());
11
12 // Store informTime array as instance variable for use in DFS
13 this.informTime = informTime;
14
15 // Build the tree structure: for each employee, add them to their manager's list
16 for (int employee = 0; employee < n; employee++) {
17 if (manager[employee] >= 0) {
18 // Add current employee to their manager's subordinate list
19 adjacencyList[manager[employee]].add(employee);
20 }
21 }
22
23 // Start DFS from the head of the company to find maximum time needed
24 return dfs(headID);
25 }
26
27 /**
28 * Performs depth-first search to calculate the maximum time needed
29 * to inform all subordinates starting from the given manager.
30 *
31 * @param managerId The ID of the current manager
32 * @return The maximum time needed to inform all subordinates in this subtree
33 */
34 private int dfs(int managerId) {
35 int maxTime = 0;
36
37 // Iterate through all direct subordinates of the current manager
38 for (int subordinate : adjacencyList[managerId]) {
39 // Recursively calculate time for each subordinate's subtree
40 // Add current manager's inform time to the subordinate's subtree time
41 maxTime = Math.max(maxTime, dfs(subordinate) + informTime[managerId]);
42 }
43
44 // Return the maximum time among all paths in this subtree
45 return maxTime;
46 }
47}
48
1class Solution {
2public:
3 int numOfMinutes(int n, int headID, vector<int>& manager, vector<int>& informTime) {
4 // Build adjacency list where each manager points to their direct subordinates
5 vector<vector<int>> adjacencyList(n);
6 for (int employee = 0; employee < n; ++employee) {
7 if (manager[employee] >= 0) {
8 adjacencyList[manager[employee]].push_back(employee);
9 }
10 }
11
12 // DFS function to calculate maximum time needed from current node
13 function<int(int)> calculateMaxTime = [&](int currentEmployee) -> int {
14 int maxTime = 0;
15
16 // For each direct subordinate of current employee
17 for (int subordinate : adjacencyList[currentEmployee]) {
18 // Recursively calculate time for subordinate's subtree
19 // Add current employee's inform time to the result
20 maxTime = max(maxTime, calculateMaxTime(subordinate) + informTime[currentEmployee]);
21 }
22
23 return maxTime;
24 };
25
26 // Start DFS from the head of the company
27 return calculateMaxTime(headID);
28 }
29};
30
1/**
2 * Calculates the minimum number of minutes needed to inform all employees in a company hierarchy
3 * @param n - Total number of employees
4 * @param headID - The ID of the head of the company
5 * @param manager - Array where manager[i] is the direct manager of employee i (-1 if no manager)
6 * @param informTime - Array where informTime[i] is the time needed for employee i to inform all direct subordinates
7 * @returns The minimum number of minutes needed to inform all employees
8 */
9function numOfMinutes(n: number, headID: number, manager: number[], informTime: number[]): number {
10 // Build adjacency list representing the company hierarchy
11 // graph[i] contains all direct subordinates of employee i
12 const graph: number[][] = new Array(n).fill(0).map(() => []);
13
14 // Populate the graph with manager-subordinate relationships
15 for (let employeeId = 0; employeeId < n; employeeId++) {
16 if (manager[employeeId] !== -1) {
17 // Add current employee as a subordinate of their manager
18 graph[manager[employeeId]].push(employeeId);
19 }
20 }
21
22 /**
23 * Performs depth-first search to calculate the maximum time needed
24 * to inform all employees in the subtree rooted at currentEmployee
25 * @param currentEmployee - The current employee node being processed
26 * @returns Maximum time to inform all employees in this subtree
27 */
28 const calculateMaxInformTime = (currentEmployee: number): number => {
29 let maxTime = 0;
30
31 // Iterate through all direct subordinates of the current employee
32 for (const subordinate of graph[currentEmployee]) {
33 // Recursively calculate time for each subordinate's subtree
34 // Add current employee's inform time to the subordinate's subtree time
35 maxTime = Math.max(maxTime, calculateMaxInformTime(subordinate) + informTime[currentEmployee]);
36 }
37
38 return maxTime;
39 };
40
41 // Start DFS from the head of the company
42 return calculateMaxInformTime(headID);
43}
44
Time and Space Complexity
Time Complexity: O(n)
The algorithm builds an adjacency list representation of the employee hierarchy tree and then performs a depth-first search (DFS) traversal. Building the adjacency list requires iterating through the manager
array once, which takes O(n)
time. During the DFS traversal, each employee node is visited exactly once. At each node, we iterate through its direct subordinates and recursively calculate the maximum time needed. Since each employee appears exactly once in the tree structure, the total number of recursive calls is n
, and the total work done across all iterations of the inner loop is also O(n)
. Therefore, the overall time complexity is O(n) + O(n) = O(n)
.
Space Complexity: O(n)
The space complexity consists of two main components. First, the adjacency list g
stores the tree structure, where each employee (except the head) appears once as a child in some manager's list, requiring O(n)
space in total. Second, the recursive DFS call stack can go as deep as the height of the tree. In the worst case (a completely unbalanced tree forming a chain), the recursion depth could be O(n)
. In the best case (a balanced tree), it would be O(log n)
. However, considering the worst-case scenario, the space complexity is O(n)
for the adjacency list plus O(n)
for the call stack, resulting in an overall space complexity of O(n)
.
Learn more about how to find time and space complexity quickly.
Common Pitfalls
1. Confusing Information Flow Direction
A common mistake is misunderstanding how information flows and when to add informTime[i]
. Students often incorrectly add the inform time of the subordinate instead of the manager.
Incorrect approach:
def dfs(i: int) -> int:
ans = 0
for j in g[i]:
# Wrong: Adding subordinate's inform time
ans = max(ans, dfs(j) + informTime[j])
return ans
Why it's wrong: The time to inform employee j
depends on their manager's informTime
, not their own. Employee j
uses informTime[j]
to inform their own subordinates, not to receive information.
Correct approach:
def dfs(i: int) -> int:
ans = 0
for j in g[i]:
# Correct: Adding current employee's inform time
ans = max(ans, dfs(j) + informTime[i])
return ans
2. Forgetting to Handle Leaf Nodes
Some implementations try to optimize by checking if an employee has no subordinates, but this can lead to errors:
Problematic approach:
def dfs(i: int) -> int:
if i not in g: # Trying to handle leaf nodes
return informTime[i] # Wrong!
ans = 0
for j in g[i]:
ans = max(ans, dfs(j) + informTime[i])
return ans
Why it's wrong: Leaf employees don't inform anyone, so their informTime
shouldn't be added to the total. The correct base case naturally returns 0 when the loop doesn't execute.
3. Building the Adjacency List Incorrectly
A frequent error is building the adjacency list in the wrong direction:
Incorrect approach:
# Wrong: Mapping employee to their manager
g = defaultdict(list)
for i, x in enumerate(manager):
if x != -1:
g[i].append(x) # Wrong direction!
Correct approach:
# Correct: Mapping manager to their subordinates
g = defaultdict(list)
for i, x in enumerate(manager):
g[x].append(i) # Manager x manages employee i
4. Not Using defaultdict and Getting KeyError
Without defaultdict
, accessing non-existent keys causes errors:
Problematic approach:
g = {} # Regular dictionary
for i, x in enumerate(manager):
if x not in g:
g[x] = []
g[x].append(i)
def dfs(i: int) -> int:
ans = 0
for j in g[i]: # KeyError if i has no subordinates!
ans = max(ans, dfs(j) + informTime[i])
return ans
Solution: Either use defaultdict(list)
or check for key existence:
def dfs(i: int) -> int:
ans = 0
if i in g: # Check if employee has subordinates
for j in g[i]:
ans = max(ans, dfs(j) + informTime[i])
return ans
What are the two properties the problem needs to have for dynamic programming to be applicable? (Select 2)
Recommended Readings
Everything About Trees A tree is a type of graph data structure composed of nodes and edges Its main properties are It is acyclic doesn't contain any cycles There exists a path from the root to any node Has N 1 edges where N is the number of nodes in the tree and
https assets algo monster cover_photos dfs svg Depth First Search Prereqs Recursion Review problems recursion_intro Trees problems tree_intro With a solid understanding of recursion under our belts we are now ready to tackle one of the most useful techniques in coding interviews Depth First Search DFS As the name suggests
https assets algo monster cover_photos bfs svg Breadth First Search on Trees Hopefully by this time you've drunk enough DFS Kool Aid to understand its immense power and seen enough visualization to create a call stack in your mind Now let me introduce the companion spell Breadth First Search BFS
Want a Structured Path to Master System Design Too? Don’t Miss This!