You are given a list of employees in a company. Each employee has a unique id
and a managerId
indicating the employee they report to. The company's structure forms a tree (no cycles, one root). Your task is to compute, for every employee, the total number of employees that report to them directly or indirectly (i.e., in their subtree), excluding themselves.
managerId = -1
).id
and managerId
values are integers.id
to the number of reports for each employee.Constraints: The tree is valid and connected. All IDs are unique. There is one valid solution.
The core of this problem is to count, for each employee, how many employees are in their management chain beneath them (excluding themselves). At first glance, you might consider, for each employee, traversing the entire tree to find all their reports. However, this approach would be inefficient, especially for large trees, since it would repeat work for overlapping subtrees.
Instead, we can recognize that the company structure is a tree, and for each employee, their total reports is the sum of the reports of their direct subordinates, plus the number of direct subordinates themselves. This suggests a recursive (or stack-based) post-order traversal, where we compute the answer for children before their manager.
To make lookups and traversals fast, it's helpful to build a mapping from each manager to their direct reports. This allows us to efficiently traverse the tree from the root down.
managerId
to a list of direct report id
s. This is often called an adjacency list in tree/graph terminology.managerId = -1
).We use a hash map (dictionary) for the adjacency list because lookups and insertions are O(1) on average. The post-order DFS ensures we only compute each subtree once, avoiding redundant work.
Input:
employees = [ { "id": 1, "managerId": -1 }, { "id": 2, "managerId": 1 }, { "id": 3, "managerId": 1 }, { "id": 4, "managerId": 2 }, { "id": 5, "managerId": 2 } ]
Tree structure:
DFS Traversal:
def count_reports(employees):
from collections import defaultdict
# Build adjacency list: managerId -> [list of direct reports]
tree = defaultdict(list)
id_set = set()
root = None
for emp in employees:
emp_id = emp['id']
mgr = emp['managerId']
id_set.add(emp_id)
if mgr == -1:
root = emp_id
else:
tree[mgr].append(emp_id)
# Result dictionary
reports = {}
def dfs(emp_id):
total = 0
for sub in tree.get(emp_id, []):
total += 1 + dfs(sub)
reports[emp_id] = total
return total
dfs(root)
return reports
#include <vector>
#include <unordered_map>
using namespace std;
struct Employee {
int id;
int managerId;
};
void dfs(int empId, unordered_map<int, vector<int>>& tree, unordered_map<int, int>& reports) {
int total = 0;
for (int sub : tree[empId]) {
dfs(sub, tree, reports);
total += 1 + reports[sub];
}
reports[empId] = total;
}
unordered_map<int, int> countReports(vector<Employee>& employees) {
unordered_map<int, vector<int>> tree;
int root = -1;
for (auto& emp : employees) {
if (emp.managerId == -1) {
root = emp.id;
} else {
tree[emp.managerId].push_back(emp.id);
}
}
unordered_map<int, int> reports;
dfs(root, tree, reports);
return reports;
}
import java.util.*;
class Employee {
int id, managerId;
Employee(int id, int managerId) {
this.id = id;
this.managerId = managerId;
}
}
public class Solution {
public Map<Integer, Integer> countReports(List<Employee> employees) {
Map<Integer, List<Integer>> tree = new HashMap<>();
int root = -1;
for (Employee emp : employees) {
if (emp.managerId == -1) {
root = emp.id;
} else {
tree.computeIfAbsent(emp.managerId, k -> new ArrayList<>()).add(emp.id);
}
}
Map<Integer, Integer> reports = new HashMap<>();
dfs(root, tree, reports);
return reports;
}
private int dfs(int empId, Map<Integer, List<Integer>> tree, Map<Integer, Integer> reports) {
int total = 0;
for (int sub : tree.getOrDefault(empId, new ArrayList<>())) {
total += 1 + dfs(sub, tree, reports);
}
reports.put(empId, total);
return total;
}
}
function countReports(employees) {
const tree = {};
let root = null;
for (const emp of employees) {
if (emp.managerId === -1) {
root = emp.id;
} else {
if (!tree[emp.managerId]) tree[emp.managerId] = [];
tree[emp.managerId].push(emp.id);
}
}
const reports = {};
function dfs(empId) {
let total = 0;
if (tree[empId]) {
for (const sub of tree[empId]) {
total += 1 + dfs(sub);
}
}
reports[empId] = total;
return total;
}
dfs(root);
return reports;
}
n
employees, this results in O(n^2) time complexity.
The optimized approach is efficient and scalable for large trees.
This problem leverages tree traversal and mapping techniques to efficiently compute the number of reports for each employee. By building an adjacency list and using post-order DFS, we avoid redundant computations and ensure that each employee's report count is calculated only once. The elegance of the solution comes from recognizing the recursive nature of the problem and using a hash map to represent the reporting structure.