LeetCode 1376. 通知所有员工所需的时间
题目描述


题意分析
员工的管理关系是一棵以
headID为根的树。负责人在时刻零已经知道消息,员工收到消息后,再用自己的informTime分钟通知全部直属下属。同一上级的直属下属在这段时间后一起收到消息,各分支随后并行向下传播。要求的是最后一名员工收到消息的时刻,不是所有人的通知时间总和,也不是管理树的层数。
解法:树上 BFS 传播收到时刻
核心思路
[!blue]
输入
manager[i]给出了员工的上级,而传播需要从上级找到下属,因此先反向建立每个员工的直属下属列表。负责人对应的上级为-1,这一项不作为普通下标使用。队列状态保存员工编号和他收到消息的时刻
receiveTime。如果当前员工在时刻t收到消息,那么他的每个直属下属都会在t + informTime[employee]收到;这里增加的是发出通知的上级所需时间,不是下属自己的时间。管理关系是树,每个员工从负责人出发的路径唯一。因此收到时刻就是这条路径上各祖先通知时间的总和,沿树传递一次就能得到准确结果,无需在多条路线间比较,也无需反复更新同一员工。
队列用于遍历管理树,并不保证按收到时刻排序,因为不同上级的通知时间可能不同。每取出一个状态就更新最大收到时刻,直到全部员工处理完;所有人并行传播,所以全局完成时间取各路径到达时刻的最大值。
最末层员工已经收到消息就满足目标,不需要再加上他们自己的后续通知时间。公司只有负责人时,初始时刻零自然就是答案。
解题步骤
- 为每名员工准备下属列表,遍历
manager,将非负责人加入其上级的列表。- 把负责人状态
(headID, 0)放入队列,初始化答案为零。- 取出员工和收到时刻,更新全局最大值。
- 将每个直属下属连同
receiveTime + informTime[employee]加入队列。- 所有状态处理完成后,返回最晚收到消息的时刻。
代码实现
class Solution {
public int numOfMinutes(int n, int headID, int[] manager, int[] informTime) {
// 按上级建立下属表,便于自顶向下传播。
List<List<Integer>> g = new ArrayList<>(n);
for (int i = 0; i < n; i++) {
g.add(new ArrayList<>());
}
for (int i = 0; i < n; i++) {
int m = manager[i];
if (m != -1) {
g.get(m).add(i);
}
}
Queue<int[]> queue = new ArrayDeque<>();
// 负责人在时刻零已经收到消息,起点不一定是编号零。
queue.offer(new int[] {
headID,
0
});
int answer = 0;
while (!queue.isEmpty()) {
int[] cur = queue.poll();
int employee = cur[0];
int receiveTime = cur[1];
// 分支并行,只取最晚收到消息的时刻。
answer = Math.max(answer, receiveTime);
// 下属收到时刻加的是当前上级的通知时间。
for (int subordinate : g.get(employee)) {
queue.offer(new int[] {
subordinate,
receiveTime + informTime[employee]
});
}
}
return answer;
}
}
func numOfMinutes(n int, headID int, manager []int, informTime []int) int {
// 按上级建立直属下属列表,便于自顶向下传播。
g := make([][]int, n)
for i := range manager {
if manager[i] != -1 {
g[manager[i]] = append(g[manager[i]], i)
}
}
type state struct {
employee, receiveTime int
}
// 负责人在时刻零已经收到消息,起点不一定是编号零。
queue := []state{
{headID, 0},
}
answer := 0
for head := 0; head < len(queue); head++ {
cur := queue[head]
// 分支并行,只取最晚收到消息的时刻。
if cur.receiveTime > answer {
answer = cur.receiveTime
}
// 下属收到时刻加的是当前上级的通知时间。
for _, subordinate := range g[cur.employee] {
queue = append(queue, state{
subordinate,
cur.receiveTime + informTime[cur.employee],
})
}
}
return answer
}
复杂度分析
设员工数为 $n$。
- 时间复杂度:$O(n)$,建立下属表和传播消息都只处理每名员工、每条管理边一次。
- 辅助空间复杂度:$O(n)$,下属表和队列状态总规模均为线性;Go 的队列保留已经读取的前缀,也被该上界包含。
关键点总结
[!green]
- 状态记录收到消息的时刻,向下传递时增加当前上级的通知时间。
- 唯一管理路径上的耗时相加,不同分支并行,因此全局取最大值。
- BFS 在这里负责遍历,不依赖队列按时间递增。
易错点总结
[!yellow]
- 传递给下属时加
informTime[subordinate],会把耗时算到错误的节点上。- 从编号零开始可能选错根,必须使用给定的
headID。- 同一上级有多个下属,不代表其通知时间要乘以下属人数,他们在同一时刻收到消息。
- 用层数代替时间会忽略不同管理边的耗时差异。
- 建下属表时要跳过
manager[headID] == -1,不能把它当作数组下标。
相似题目
| 题目 | 难度 | 关联与区别 |
|---|---|---|
| 690. 员工的重要性 | 中等 | 同样按员工树递归,本题取到各下属的最大传播时间,不能像重要度那样把所有分支时间直接相加。 |
| 743. 网络延迟时间 | 中等 | 最终都取最晚到达时间,本题组织结构是树且路径唯一,可直接沿树累计而不必做一般图最短路。 |
转载与许可
许可
本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议,转载请注明出处!