公司里有 n
名员工,每个员工的 ID 都是独一无二的,编号从 0
到 n - 1
。公司的总负责人通过 headID
进行标识。
在 manager
数组中,每个员工都有一个直属负责人,其中 manager[i]
是第 i
名员工的直属负责人。对于总负责人,manager[headID] = -1
。题目保证从属关系可以用树结构显示。
公司总负责人想要向公司所有员工通告一条紧急消息。他将会首先通知他的直属下属们,然后由这些下属通知他们的下属,直到所有的员工都得知这条紧急消息。
第 i
名员工需要 informTime[i]
分钟来通知它的所有直属下属(也就是说在 informTime[i]
分钟后,他的所有直属下属都可以开始传播这一消息)。
返回通知所有员工这一紧急消息所需要的 分钟数 。
示例 1:
**输入:** n = 1, headID = 0, manager = [-1], informTime = [0]
**输出:** 0
**解释:** 公司总负责人是该公司的唯一一名员工。
示例 2:
![](https://assets.leetcode-cn.com/aliyun-lc- upload/uploads/2020/03/08/graph.png)
**输入:** n = 6, headID = 2, manager = [2,2,-1,2,2,2], informTime = [0,0,1,0,0,0]
**输出:** 1
**解释:** id = 2 的员工是公司的总负责人,也是其他所有员工的直属负责人,他需要 1 分钟来通知所有员工。
上图显示了公司员工的树结构。
提示:
1 <= n <= 10^5
0 <= headID < n
manager.length == n
0 <= manager[i] < n
manager[headID] == -1
informTime.length == n
0 <= informTime[i] <= 1000
如果员工 i
没有下属,informTime[i] == 0
。
题目 保证 所有员工都可以收到通知。
问题提示
如何用代码表示员工之间的从属关系?
如何在代码中表示树形结构,并遍历整棵树?
如何优化遍历的效率,避免重复计算通知时间?
前置知识
树的遍历,包括深度优先遍历(DFS)和广度优先遍历(BFS);
递归思想,包括递归函数的定义、调用、返回等;
一些基本的数据结构,例如数组、哈希表等。
解题思路 方法一:深度优先搜索 思路与算法
题目保证员工的从属关系可以用树结构显示,所以我们可以根据数组 manager 来构建树模型,其中每一个员工为一个节点,每一个员工的上司为其父节点,总负责人为根节点。我们存储每一个节点的子节点,然后我们可以通过「自顶向下」的方式,从根节点开始往下「深度优先搜索」来传递信息传递的过程,计算从每一个节点往下传递信息的所需要的最大时间。
代码
[sol1-Python3] 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 class Solution : def numOfMinutes (self, n: int , headID: int , manager: List [int ], informTime: List [int ] ) -> int : g = collections.defaultdict(list ) for i in range (n): g[manager[i]].append(i) def dfs (cur ): res = 0 for ne in g[cur]: res = max (res, dfs(ne)) return informTime[cur] + res return dfs(headID)
[sol1-Java] 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 class Solution { public int numOfMinutes (int n, int headID, int [] manager, int [] informTime) { Map<Integer, List<Integer>> g = new HashMap <Integer, List<Integer>>(); for (int i = 0 ; i < n; i++) { g.putIfAbsent(manager[i], new ArrayList <Integer>()); g.get(manager[i]).add(i); } return dfs(headID, informTime, g); } public int dfs (int cur, int [] informTime, Map<Integer, List<Integer>> g) { int res = 0 ; for (int neighbor : g.getOrDefault(cur, new ArrayList <Integer>())) { res = Math.max(res, dfs(neighbor, informTime, g)); } return informTime[cur] + res; } }
[sol1-C#] 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 public class Solution { public int NumOfMinutes (int n, int headID, int [] manager, int [] informTime ) { IDictionary<int , IList<int >> g = new Dictionary<int , IList<int >>(); for (int i = 0 ; i < n; i++) { g.TryAdd(manager[i], new List<int >()); g[manager[i]].Add(i); } return DFS(headID, informTime, g); } public int DFS (int cur, int [] informTime, IDictionary<int , IList<int >> g ) { int res = 0 ; if (g.ContainsKey(cur)) { foreach (int neighbor in g[cur]) { res = Math.Max(res, DFS(neighbor, informTime, g)); } } return informTime[cur] + res; } }
[sol1-C++] 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 class Solution {public : int numOfMinutes (int n, int headID, vector<int >& manager, vector<int >& informTime) { unordered_map<int , vector<int >> g; for (int i = 0 ; i < n; i++) { g[manager[i]].emplace_back (i); } function<int (int )> dfs = [&](int cur) -> int { int res = 0 ; for (int neighbor : g[cur]) { res = max (res, dfs (neighbor)); } return informTime[cur] + res; }; return dfs (headID); } };
[sol1-C] 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 typedef struct { int key; struct ListNode *list ; UT_hash_handle hh; } HashItem; static int max (int a, int b) { return a > b ? a : b; } struct ListNode *listNodeCreat (int val) { struct ListNode *obj = (struct ListNode *)malloc (sizeof (struct ListNode)); obj->val = val; obj->next = NULL ; return obj; } void listFree (struct ListNode *head) { while (head) { struct ListNode *cur = head; head = head->next; free (cur); } } HashItem *hashFindItem (HashItem **obj, int key) { HashItem *pEntry = NULL ; HASH_FIND_INT(*obj, &key, pEntry); return pEntry; } bool hashAddItem (HashItem **obj, int key, int val) { HashItem *pEntry = hashFindItem(obj, key); if (pEntry != NULL ) { struct ListNode *cur = listNodeCreat(val); cur->next = pEntry->list ; pEntry->list = cur; } else { pEntry = (HashItem *)malloc (sizeof (HashItem)); pEntry->key = key; pEntry->list = listNodeCreat(val); HASH_ADD_INT(*obj, key, pEntry); } return true ; } struct ListNode * hashGetItem (HashItem **obj, int key) { HashItem *pEntry = hashFindItem(obj, key); if (!pEntry) { return NULL ; } else { return pEntry->list ; } } void hashFree (HashItem **obj) { HashItem *curr = NULL , *tmp = NULL ; HASH_ITER(hh, *obj, curr, tmp) { HASH_DEL(*obj, curr); listFree(curr->list ); free (curr); } } int dfs (int cur, const int * informTime, HashItem **g) { int res = 0 ; HashItem *pEntry = hashFindItem(g, cur); if (pEntry) { for (struct ListNode *node = pEntry->list ; node; node = node->next) { int neighbor = node->val; res = max(res, dfs(neighbor, informTime, g)); } } return informTime[cur] + res; } int numOfMinutes (int n, int headID, int * manager, int managerSize, int * informTime, int informTimeSize) { HashItem *g = NULL ; for (int i = 0 ; i < n; i++) { hashAddItem(&g, manager[i], i); } int ret = dfs(headID, informTime, &g); return ret; }
[sol1-JavaScript] 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 var numOfMinutes = function (n, headID, manager, informTime ) { const g = new Map (); const dfs = (cur, informTime, g ) => { let res = 0 ; for (const neighbor of g.get (cur) || []) { res = Math .max (res, dfs (neighbor, informTime, g)); } return informTime[cur] + res; }; for (let i = 0 ; i < n; i++) { if (!g.has (manager[i])) { g.set (manager[i], []); } g.get (manager[i]).push (i); } return dfs (headID, informTime, g); }
[sol1-Golang] 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 func numOfMinutes (n int , headID int , manager []int , informTime []int ) int { g := map [int ][]int {} for i, m := range manager { g[m] = append (g[m], i) } var dfs func (int ) int dfs = func (cur int ) (res int ) { for _, neighbor := range g[cur] { res1 := dfs(neighbor) if res1 > res { res = res1 } } return informTime[cur] + res } return dfs(headID) }
复杂度分析
时间复杂度:O(n)。
空间复杂度:O(n),主要为建图的空间开销。
方法二:广度优先搜索 思路与算法
同样我们可以用「广度优先搜索」来实现「方法一」中信息传递的过程。每一个员工看作一个节点,总负责人为根节点,队列中存放二元组 (\textit{node}, \textit{time}),其中 node 表示当前员工,time 为信息传递到该员工的时间。最初我们将二元组 (\textit{headID}, 0) 放入队列,每一个叶子节点的最大时间即为答案。
代码
[sol2-Python3] 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 import queueclass Solution : def numOfMinutes (self, n: int , headID: int , manager: List [int ], informTime: List [int ] ) -> int : g = collections.defaultdict(list ) for i in range (n): g[manager[i]].append(i) q = queue.Queue() q.put((headID, 0 )) res = 0 while not q.empty(): tmp_id, val = q.get() if len (g[tmp_id]) == 0 : res = max (res, val) else : for ne in g[tmp_id]: q.put((ne, val + informTime[tmp_id])) return res
[sol2-Java] 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 class Solution { public int numOfMinutes (int n, int headID, int [] manager, int [] informTime) { Map<Integer, List<Integer>> g = new HashMap <Integer, List<Integer>>(); for (int i = 0 ; i < n; i++) { g.putIfAbsent(manager[i], new ArrayList <Integer>()); g.get(manager[i]).add(i); } Queue<int []> queue = new ArrayDeque <int []>(); queue.offer(new int []{headID, 0 }); int res = 0 ; while (!queue.isEmpty()) { int [] arr = queue.poll(); int tmpId = arr[0 ], val = arr[1 ]; if (!g.containsKey(tmpId)) { res = Math.max(res, val); } else { for (int ne : g.get(tmpId)) { queue.offer(new int []{ne, val + informTime[tmpId]}); } } } return res; } }
[sol2-C#] 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 public class Solution { public int NumOfMinutes (int n, int headID, int [] manager, int [] informTime ) { IDictionary<int , IList<int >> g = new Dictionary<int , IList<int >>(); for (int i = 0 ; i < n; i++) { g.TryAdd(manager[i], new List<int >()); g[manager[i]].Add(i); } Queue<int []> queue = new Queue<int []>(); queue.Enqueue(new int []{headID, 0 }); int res = 0 ; while (queue.Count > 0 ) { int [] arr = queue.Dequeue(); int tmpId = arr[0 ], val = arr[1 ]; if (!g.ContainsKey(tmpId)) { res = Math.Max(res, val); } else { foreach (int ne in g[tmpId]) { queue.Enqueue(new int []{ne, val + informTime[tmpId]}); } } } return res; } }
[sol2-C++] 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 class Solution {public : int numOfMinutes (int n, int headID, vector<int >& manager, vector<int >& informTime) { unordered_map<int , vector<int >> g; for (int i = 0 ; i < n; i++) { g[manager[i]].emplace_back (i); } queue<pair<int , int >> q; q.emplace (headID, 0 ); int res = 0 ; while (!q.empty ()) { auto [tmp_id, val] = q.front (); q.pop (); if (!g.count (tmp_id)) { res = max (res, val); } else { for (int neighbor : g[tmp_id]) { q.emplace (neighbor, val + informTime[tmp_id]); } } } return res; } };
[sol2-C] 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 typedef struct { int key; struct ListNode *list ; UT_hash_handle hh; } HashItem; static int max (int a, int b) { return a > b ? a : b; } struct ListNode *listNodeCreat (int val) { struct ListNode *obj = (struct ListNode *)malloc (sizeof (struct ListNode)); obj->val = val; obj->next = NULL ; return obj; } void listFree (struct ListNode *head) { while (head) { struct ListNode *cur = head; head = head->next; free (cur); } } HashItem *hashFindItem (HashItem **obj, int key) { HashItem *pEntry = NULL ; HASH_FIND_INT(*obj, &key, pEntry); return pEntry; } bool hashAddItem (HashItem **obj, int key, int val) { HashItem *pEntry = hashFindItem(obj, key); if (pEntry != NULL ) { struct ListNode *cur = listNodeCreat(val); cur->next = pEntry->list ; pEntry->list = cur; } else { pEntry = (HashItem *)malloc (sizeof (HashItem)); pEntry->key = key; pEntry->list = listNodeCreat(val); HASH_ADD_INT(*obj, key, pEntry); } return true ; } struct ListNode * hashGetItem (HashItem **obj, int key) { HashItem *pEntry = hashFindItem(obj, key); if (!pEntry) { return NULL ; } else { return pEntry->list ; } } void hashFree (HashItem **obj) { HashItem *curr = NULL , *tmp = NULL ; HASH_ITER(hh, *obj, curr, tmp) { HASH_DEL(*obj, curr); listFree(curr->list ); free (curr); } } int numOfMinutes (int n, int headID, int * manager, int managerSize, int * informTime, int informTimeSize) { HashItem *g = NULL ; for (int i = 0 ; i < n; i++) { hashAddItem(&g, manager[i], i); } int queue [n][2 ]; int head = 0 , tail = 0 ; queue [tail][0 ] = headID; queue [tail][1 ] = 0 ; tail++; int res = 0 ; while (head != tail) { int tmp_id = queue [head][0 ]; int val = queue [head][1 ]; head++; HashItem *pEntry = hashFindItem(&g, tmp_id); if (pEntry) { for (struct ListNode *node = pEntry->list ; node; node = node->next) { int neighbor = node->val; queue [tail][0 ] = neighbor; queue [tail][1 ] = val + informTime[tmp_id]; tail++; } } else { res = max(res, val); } } hashFree(&g); return res; }
[sol2-JavaScript] 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 var numOfMinutes = function (n, headID, manager, informTime ) { const g = new Map (); for (let i = 0 ; i < n; i++) { if (!g.has (manager[i])) { g.set (manager[i], []); } g.get (manager[i]).push (i); } const queue = []; queue.push ([headID, 0 ]); let res = 0 ; while (queue.length ) { const arr = queue.shift (); const tmpId = arr[0 ], val = arr[1 ]; if (!g.has (tmpId)) { res = Math .max (res, val); } else { for (const ne of g.get (tmpId)) { queue.push ([ne, val + informTime[tmpId]]); } } } return res; }
复杂度分析
时间复杂度:O(n)。
空间复杂度:O(n),主要为建图的空间开销。
方法三:记忆化搜索 思路与算法
上述「方法一」和「方法二」都是「自顶向下」的实现方式。同样我们可以通过「自底向上」的方式,从每一个员工开始往上进行搜索,记录每一个员工到根节点(员工总负责人)所需要的时间,其中所需要的最大时间即为答案。由于每一个员工到根节点的路径唯一,且每一个员工可能有多个下属,所以为了避免重复计算,我们可以通过「记忆化」的方式来进行处理。
代码
[sol3-Python3] 1 2 3 4 5 6 7 8 9 10 11 12 class Solution : def numOfMinutes (self, n: int , headID: int , manager: List [int ], informTime: List [int ] ) -> int : @cache def dfs (cur ): if cur == headID: return 0 return dfs(manager[cur]) + informTime[manager[cur]] return max (dfs(i) for i in range (n))
[sol3-Java] 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 class Solution { int headID; int [] manager; int [] informTime; Map<Integer, Integer> memo = new HashMap <Integer, Integer>(); public int numOfMinutes (int n, int headID, int [] manager, int [] informTime) { this .headID = headID; this .manager = manager; this .informTime = informTime; int res = 0 ; for (int i = 0 ; i < n; i++) { res = Math.max(res, dfs(i)); } return res; } public int dfs (int cur) { if (cur == headID) { return 0 ; } if (!memo.containsKey(cur)) { int res = dfs(manager[cur]) + informTime[manager[cur]]; memo.put(cur, res); } return memo.get(cur); } }
[sol3-C#] 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 public class Solution { int headID; int [] manager; int [] informTime; IDictionary<int , int > memo = new Dictionary<int , int >(); public int NumOfMinutes (int n, int headID, int [] manager, int [] informTime ) { this .headID = headID; this .manager = manager; this .informTime = informTime; int res = 0 ; for (int i = 0 ; i < n; i++) { res = Math.Max(res, DFS(i)); } return res; } public int DFS (int cur ) { if (cur == headID) { return 0 ; } if (!memo.ContainsKey(cur)) { int res = DFS(manager[cur]) + informTime[manager[cur]]; memo.Add(cur, res); } return memo[cur]; } }
[sol3-C++] 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 class Solution {public : int numOfMinutes (int n, int headID, vector<int >& manager, vector<int >& informTime) { unordered_map<int , int > memo; function<int (int )> dfs = [&](int cur) -> int { if (cur == headID) { return 0 ; } if (memo.count (cur)) { return memo[cur]; } int res = dfs (manager[cur]) + informTime[manager[cur]]; memo[cur] = res; return res; }; int res = 0 ; for (int i = 0 ; i < n; i++) { res = max (res, dfs (i)); } return res; } };
[sol3-C] 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 typedef struct { int key; int val; UT_hash_handle hh; } HashItem; HashItem *hashFindItem (HashItem **obj, int key) { HashItem *pEntry = NULL ; HASH_FIND_INT(*obj, &key, pEntry); return pEntry; } bool hashAddItem (HashItem **obj, int key, int val) { if (hashFindItem(obj, key)) { return false ; } HashItem *pEntry = (HashItem *)malloc (sizeof (HashItem)); pEntry->key = key; pEntry->val = val; HASH_ADD_INT(*obj, key, pEntry); return true ; } bool hashSetItem (HashItem **obj, int key, int val) { HashItem *pEntry = hashFindItem(obj, key); if (!pEntry) { hashAddItem(obj, key, val); } else { pEntry->val = val; } return true ; } int hashGetItem (HashItem **obj, int key, int defaultVal) { HashItem *pEntry = hashFindItem(obj, key); if (!pEntry) { return defaultVal; } return pEntry->val; } void hashFree (HashItem **obj) { HashItem *curr = NULL , *tmp = NULL ; HASH_ITER(hh, *obj, curr, tmp) { HASH_DEL(*obj, curr); free (curr); } } int dfs (int cur, int headID, HashItem **memo, const int * manager, const int * informTime) { if (cur == headID) { return 0 ; } if (hashFindItem(memo, cur) != NULL ) { return hashGetItem(memo, cur, 0 ); } int res = dfs(manager[cur], headID, memo, manager, informTime) + informTime[manager[cur]]; hashAddItem(memo, cur, res); return res; } int numOfMinutes (int n, int headID, int * manager, int managerSize, int * informTime, int informTimeSize) { HashItem *memo = NULL ; int res = 0 ; for (int i = 0 ; i < n; i++) { res = max(res, dfs(i, headID, &memo, manager, informTime)); } hashFree(&memo); return res; }
[sol3-JavaScript] 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 var numOfMinutes = function (n, headID, manager, informTime ) { let res = 0 ; const memo = new Map (); const dfs = (cur ) => { if (cur === headID) { return 0 ; } if (!memo.has (cur)) { const res = dfs (manager[cur]) + informTime[manager[cur]]; memo.set (cur, res); } return memo.get (cur); } for (let i = 0 ; i < n; i++) { res = Math .max (res, dfs (i)); } return res; }
复杂度分析
时间复杂度:O(n)。
空间复杂度:O(n),主要为对记忆化的空间开销。
练习题目推荐
拓展思考 本题中每个人的信息处理时间都是一样的,如果不同的人处理时间不一样,能否用类似的算法解决问题呢?