690. Employee Importance


Problem Description

The problem revolves around calculating a collective importance score for an employee and their entire reporting hierarchy within a company. There is a data structure for employee information where each employee has a unique ID, a numeric value representing their importance, and a list of IDs for their direct subordinates. Our goal is to find the total importance value for a single employee specified by their ID, along with the importance of their direct and indirect subordinates. Essentially, we are tasked with performing a sum of importance values that spans across multiple levels of the employee reporting chain.

Intuition

To solve this problem, we need a way to traverse through an employee's subordinates and all of their subsequent subordinates recursively until there are none left. We should think of each employee as a node in a tree, where the given employee is the root, and their direct and indirect subordinates form the branches and leaves.

The first step is to map all the employees by their IDs for quick access. This is because we're provided with a list, and checking each employee's ID to find their subordinates would take a lot of time especially if there are many employees.

With this mapping in place, we'll use recursion, which is a natural fit for this tree-like structure. The idea of the recursive depth-first search (DFS) is to start with the given employee ID, find the employee's importance, and then call the function recursively for each of its subordinates. This process will sum up all the importance values from the bottom of the hierarchy (the leaf nodes with no subordinates) all the way to the top (the employee whose ID was provided).

An alternative approach could also be using breadth-first search (BFS), where we process all direct subordinates first before moving on to the next level down in the hierarchy. However, the provided solution uses DFS, which is more intuitive for such hierarchical structures, often yielding simpler and more concise code.

Learn more about Depth-First Search and Breadth-First Search patterns.

Not Sure What to Study? Take the 2-min Quiz to Find Your Missing Piece:

Is the following code DFS or BFS?

1void search(Node root) {
2  if (!root) return;
3  visit(root);
4  root.visited = true;
5  for (Node node in root.adjacent) {
6    if (!node.visited) {
7      search(node);
8    }
9  }
10}

Solution Approach

The solution employs a depth-first search (DFS) algorithm which is a common technique used to traverse tree or graph data structures. Here's a step-by-step explanation of the implementation:

  1. Hash Table Creation: We start by creating a hash table (in Python, a dictionary) to map each employee's ID to the corresponding employee object. This mapping allows for quick access to any employee's details when given their ID, which is essential for efficient traversal. The hash table creation is represented by the line m = {emp.id: emp for emp in employees} in the code.

  2. Recursive DFS Function: A recursive function named dfs is defined inside the Solution class. This function takes an employee's ID as an input and returns the total importance value of that employee, plus the importance of all their subordinates, both direct and indirect. This is implemented as follows:

    • Retrieve the Employee object corresponding to the given ID from the hash table.
    • Initialize a sum s with the importance of the employee.
    • Loop through the list of subordinates of the employee. For each subordinate ID, call the dfs function and add the returned importance to the sum s.
    • Return the total sum s after traversing all subordinates.
  3. Initiating the DFS Call: The getImportance function is the entry point for the solution. It calls the DFS dfs function starting with the employee ID provided as an input to the problem and returns the importance value obtained from this call.

The importance of recursion here is that it naturally follows the hierarchy structure by diving deep into each branch (subordinate chain) and unwinding the importance values as the call stack returns. This DFS approach ensures that all employees in the hierarchy are accounted for and their importances are added up correctly to yield the final answer.

The code snippet return dfs(id) initiates the process by starting the recursive traversal, passing the ID of the employee whose total importance is to be calculated.

The key algorithm and data structure pattern used here is a combination of a DFS algorithm for traversal and a hash table for efficient data access.

Discover Your Strengths and Weaknesses: Take Our 2-Minute Quiz to Tailor Your Study Plan:

What's the output of running the following function using the following tree as input?

1def serialize(root):
2    res = []
3    def dfs(root):
4        if not root:
5            res.append('x')
6            return
7        res.append(root.val)
8        dfs(root.left)
9        dfs(root.right)
10    dfs(root)
11    return ' '.join(res)
12
1import java.util.StringJoiner;
2
3public static String serialize(Node root) {
4    StringJoiner res = new StringJoiner(" ");
5    serializeDFS(root, res);
6    return res.toString();
7}
8
9private static void serializeDFS(Node root, StringJoiner result) {
10    if (root == null) {
11        result.add("x");
12        return;
13    }
14    result.add(Integer.toString(root.val));
15    serializeDFS(root.left, result);
16    serializeDFS(root.right, result);
17}
18
1function serialize(root) {
2    let res = [];
3    serialize_dfs(root, res);
4    return res.join(" ");
5}
6
7function serialize_dfs(root, res) {
8    if (!root) {
9        res.push("x");
10        return;
11    }
12    res.push(root.val);
13    serialize_dfs(root.left, res);
14    serialize_dfs(root.right, res);
15}
16

Example Walkthrough

Let's consider a small set of employee data:

  • Employee 1: Importance = 5, Subordinates = [2, 3]
  • Employee 2: Importance = 3, Subordinates = []
  • Employee 3: Importance = 6, Subordinates = [4]
  • Employee 4: Importance = 2, Subordinates = []

We are asked to calculate the total importance score for Employee 1.

Hash Table Creation:

First, we create a hash table mapping employee IDs to their respective Employee objects for quick access:

1m = {1: Employee(1, 5, [2, 3]), 2: Employee(2, 3, []), 3: Employee(3, 6, [4]), 4: Employee(4, 2, [])}

Recursive DFS Function:

The dfs function operates as follows when we call dfs(1):

  1. Look up Employee 1 in the hash table 'm' and find its importance and subordinates.
  2. Start with a sum s = 5, the importance value of Employee 1.
  3. Loop over Employee 1's subordinates (Employee 2 and 3):
    • For Employee 2: no subordinates, so just add 3 to the sum s (now s = 8).
    • For Employee 3: Call dfs for Employee 3.
      • Look up Employee 3 in hash table, get importance (6), and its subordinate (Employee 4).
      • Sum the importance for Employee 3 (s = 6).
      • Employee 3 has a subordinate, Employee 4, call dfs on Employee 4.
        • Add Employee 4's importance to the sum s (now s = 8).
      • No more subordinates, return s (8). Add this to our main sum s which is now s = 16.
  4. After traversing all subordinates of Employee 1, the total sum s is 16.

Initiating the DFS Call:

The getImportance function initiates the DFS call with return dfs(1) and would return the total importance value of 16 for Employee 1 and their entire reporting hierarchy.

Solution Implementation

1# Definition for Employee class.
2class Employee:
3    def __init__(self, id: int, importance: int, subordinates: list[int]):
4        self.id = id
5        self.importance = importance
6        self.subordinates = subordinates
7
8
9class Solution:
10    def get_importance(self, employees: list['Employee'], id: int) -> int:
11        # Create a dictionary where the key is the employee ID and the value is the employee object.
12        # This allows for quick access to any employee object.
13        employee_map = {employee.id: employee for employee in employees}
14
15        # Recursive function to calculate the total importance value starting from the given employee ID.
16        def calculate_importance(emp_id: int) -> int:
17            # Get the employee object based on the employee ID.
18            employee = employee_map[emp_id]
19            # Start with the employee's importance.
20            total_importance = employee.importance
21            # For each of the employee's subordinates, add their importance recursively.
22            for subordinate_id in employee.subordinates:
23                total_importance += calculate_importance(subordinate_id)
24            # Return the total importance accumulated.
25            return total_importance
26
27        # Call the recursive function starting from the given ID.
28        return calculate_importance(id)
29
1import java.util.HashMap;
2import java.util.List;
3import java.util.Map;
4
5// Definition for Employee class provided.
6class Employee {
7    public int id;
8    public int importance;
9    public List<Integer> subordinates;
10}
11
12class Solution {
13
14    // A hashmap to store employee id and corresponding Employee object for quick access.
15    private final Map<Integer, Employee> employeeMap = new HashMap<>();
16
17    // Calculates the total importance value of an employee and all their subordinates.
18    public int getImportance(List<Employee> employees, int id) {
19        // Populate the employeeMap with the given list of employees.
20        for (Employee employee : employees) {
21            employeeMap.put(employee.id, employee);
22        }
23        // Start the Depth-First Search (DFS) from the given employee id.
24        return dfs(id);
25    }
26
27    // Helper method to perform DFS on the employee hierarchy and accumulate importance.
28    private int dfs(int employeeId) {
29        // Retrieve the current Employee object using its id.
30        Employee employee = employeeMap.get(employeeId);
31        // Start with the importance of the current employee.
32        int totalImportance = employee.importance;
33        // Recursively accumulate the importance of each subordinate.
34        for (Integer subordinateId : employee.subordinates) {
35            totalImportance += dfs(subordinateId);
36        }
37        // Return the total importance accumulated.
38        return totalImportance;
39    }
40}
41
1#include <vector>
2#include <unordered_map>
3
4// C++ struct definition for Employee.
5struct Employee {
6  int id;
7  int importance;
8  std::vector<int> subordinates;
9};
10
11/**
12 * Calculates the total importance value of an employee and their subordinates.
13 * 
14 * @param employees - Vector of Employee pointers.
15 * @param id - The employee id for which the importance value needs to be calculated.
16 * @return The total importance value of the employee and their subordinates.
17 */
18int getImportance(std::vector<Employee*> employees, int id) {
19  // Unordered map to store employee id as key and employee pointer as value for constant time look-ups.
20  std::unordered_map<int, Employee*> employee_map;
21
22  // Fill the map with the employees.
23  for (Employee* employee : employees) {
24    employee_map[employee->id] = employee;
25  }
26
27  // Recursive helper function to calculate importance using depth-first search.
28  std::function<int(int)> dfs = [&](int employeeId) -> int {
29    // Retrieve the current employee from the map using their id.
30    auto it = employee_map.find(employeeId);
31  
32    // If the employee is not found in the map, there is no such employee with the employeeId.
33    if (it == employee_map.end()) {
34      return 0;
35    }
36  
37    Employee* employee = it->second;
38  
39    // Start with the importance of the current employee.
40    int total_importance = employee->importance;
41
42    // Include the importance of each subordinate using depth-first search.
43    for (int subId : employee->subordinates) {
44      total_importance += dfs(subId);
45    }
46  
47    return total_importance;
48  };
49
50  // Kick off the recursive depth-first search process using the provided id.
51  return dfs(id);
52}
53
54// Usage of the function remains the same as provided by the user.
55
1// TypeScript type definitions for Employee.
2
3type Employee = {
4  id: number;
5  importance: number;
6  subordinates: number[];
7}
8
9/**
10 * Calculates the total importance value of an employee and their subordinates.
11 * 
12 * @param employees - Array of Employee objects.
13 * @param id - The employee id for which the importance value needs to be calculated.
14 * @return The total importance value of the employee and their subordinates.
15 */
16const getImportance = (employees: Employee[], id: number): number => {
17  // Map to store employee id as key and employee object as value.
18  const employeeMap: Map<number, Employee> = new Map();
19
20  // Fill the map with the employees for constant time look-ups.
21  employees.forEach(employee => {
22    employeeMap.set(employee.id, employee);
23  });
24
25  /**
26   * Recursive helper function to calculate importance using depth-first search.
27   * 
28   * @param employeeId - The id of the current employee being processed.
29   * @return The total importance including all subordinates' importance.
30   */
31  const dfs = (employeeId: number): number => {
32    // Retrieve the current employee from the map using their id.
33    const employee = employeeMap.get(employeeId);
34  
35    // If employee is undefined, there is no such employee with the employeeId.
36    if (!employee) {
37      return 0;
38    }
39  
40    // Start with the importance of the current employee.
41    let totalImportance = employee.importance;
42
43    // Include the importance of each subordinate (depth-first search).
44    employee.subordinates.forEach(subId => {
45      totalImportance += dfs(subId);
46    });
47  
48    return totalImportance;
49  };
50
51  // Kick off the recursive process using the provided id.
52  return dfs(id);
53};
54
55// Usage of the function remain the same as provided by the user.
56
Not Sure What to Study? Take the 2-min Quiz:

Which algorithm is best for finding the shortest distance between two points in an unweighted graph?

Time and Space Complexity

The given code defines a Solution class with a method getImportance to calculate the total importance value of an employee and all of their subordinates recursively.

Time Complexity

The time complexity of the code is O(N), where N is the total number of employees. This is because the dfs function ensures that each employee is processed exactly once. The creation of the dictionary m is O(N) since it involves going through all the employees once and inserting them into the m mapping, and the recursive dfs calls process each employee and their subordinates once.

Space Complexity

The space complexity of the code is also O(N). This involves the space for the dictionary m which stores all the employees, corresponding to O(N). Additionally, the space complexity takes into account the recursion call stack, which in the worst case, when the organization chart forms a deep hierarchy, can go as deep as the number of employees N in the path from the top employee to the lowest-level employee.

Learn more about how to find time and space complexity quickly using problem constraints.

Fast Track Your Learning with Our Quick Skills Quiz:

How would you design a stack which has a function min that returns the minimum element in the stack, in addition to push and pop? All push, pop, min should have running time O(1).


Recommended Readings


Got a question? Ask the Teaching Assistant anything you don't understand.

Still not clear? Ask in the Forum,  Discord or Submit the part you don't understand to our editors.


TA 👨‍🏫