Facebook Pixel

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 employee i
  • 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 employee i to inform all their direct subordinates
  • After informTime[i] minutes, all direct subordinates of employee i 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:

  1. We have a tree structure representing the company hierarchy
  2. We need to calculate the time for information to propagate from the root (head) to all leaves (employees with no subordinates)
  3. The total time for any branch is the sum of inform times along that path
  4. We need to find the maximum time among all paths from root to leaves
  5. 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.

Quick Interview Experience
Help others by sharing your interview experience
Have you seen this problem before?

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 employee i, we recursively calculate dfs(j) - the time for subordinate j to inform their entire subtree
  • We add informTime[i] to this value because employee i needs this time to inform subordinate j before j 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 Evaluator

Example 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
    • dfs(1): Employee 1 has no subordinates → returns 0
      • Time through this branch: 0 + informTime[2] = 0 + 3 = 3
    • dfs(3): Employee 3 has subordinates [4, 5]
      • dfs(4): Employee 4 has no subordinates → returns 0
        • Time: 0 + informTime[3] = 0 + 2 = 2
      • dfs(5): Employee 5 has no subordinates → returns 0
        • Time: 0 + informTime[3] = 0 + 2 = 2
      • dfs(3) returns max(2, 2) = 2
      • Time through this branch: 2 + informTime[2] = 2 + 3 = 5
  • dfs(2) returns max(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
Discover Your Strengths and Weaknesses: Take Our 5-Minute Quiz to Tailor Your Study Plan:

What are the two properties the problem needs to have for dynamic programming to be applicable? (Select 2)


Recommended Readings

Want a Structured Path to Master System Design Too? Don’t Miss This!

Load More