课程表 II:拓扑序就是修课顺序,有环交空数组
先修关系是有向边。入度为 0 的课可以上;出队时写入 order。修不完 numCourses 门,说明有环,返回 []。
边 b→a 表示修 a 之前必须修 b。统计入度,入度 0 入队。出队把课追加到 order,并把后继入度减一,减到 0 再入队。order 长度等于 numCourses 则返回它,否则返回空数组。时间 O(V+E),空间 O(V+E)。
这是 LeetCode 210. Course Schedule II。共有 numCourses 门课,编号 0 到 n−1。prerequisites[i] = [ai, bi] 表示修 ai 之前必须先修 bi。返回任意一个能修完所有课的顺序;不可能则返回空数组。
主例 numCourses = 4,先修 [[1,0],[2,0],[3,1],[3,2]]。0 没有先修;1 和 2 都等 0;3 等 1 和 2。一条合法顺序是 [0,1,2,3]。1 和 2 谁先谁后都行。
第一直觉是「按编号从小到大上课」。主例碰巧 0、1、2、3 都合法,但若先修是 [0,1],编号序会先上 0,先修没满足。缺的是动态维护「现在谁的先修都修完了」。下面从这块缺口推出 Kahn 拓扑排序。
出队写入 order,写不满就是环
先修 [a,b] 是有向边 b→a:b 修完,a 的入度才能减。图画反了,入度会记到先修课头上,顺序全错。入度数组长度是课程数,不是先修条数。
缺的信息是「此刻可以上的课」。没有入度,你不知道谁的门已经打开。有入度却不出队记录,就只剩 LC207 的布尔判断。本题要把每次出队的课追加到 order:它当前没有未完成的先修,放进序列一定合法。
从缺口写出 Kahn。建邻接表和入度。所有入度 0 的课入队。当队列非空:弹出 cur,写入 order;对 graph[cur] 里每个后继,入度减一,减到 0 就入队。循环结束看长度。少一门,就是有人入度永远减不完——环。环上的课互相等待,返回 []。
用手走主例。入度 [0,1,1,2],只有课 0 就绪,ready=[0],taken 空。演示第一帧。修完 0,1 和 2 的入度变成 0,3 仍是 2。order=[0],ready=[1,2]。演示第二帧。1、2 先后出队(队列顺序决定谁先),3 的入度两次减一变成 0。order=[0,1,2],ready=[3]。演示第三帧。3 出队,order=[0,1,2,3],长度 4,返回它。
若再加一条 [0,3],0 和 3 互相等待,队列会在某步空掉而 order 写不满,返回 []。合法顺序不唯一,测试只要求拓扑约束成立。每条边减一次入度,时间 O(V+E)。DFS 三色法也可以:后序反转得到拓扑序,碰到回边就是环,同样返回 []。
空数组不是失败兜底,是有环order 长度不够,说明存在入度减不完的课。不要返回「已经排出的前缀」——前缀合法,但题目要的是修完所有课。
Go:Kahn 输出顺序
func findOrder(numCourses int, prerequisites [][]int) []int {graph := make([][]int, numCourses)indegree := make([]int, numCourses)for _, p := range prerequisites {graph[p[1]] = append(graph[p[1]], p[0])indegree[p[0]]++}q := []int{}for i := 0; i < numCourses; i++ {if indegree[i] == 0 { q = append(q, i) }}order := []int{}for len(q) > 0 {cur := q[0]; q = q[1:]order = append(order, cur)for _, nb := range graph[cur] {indegree[nb]--if indegree[nb] == 0 { q = append(q, nb) }}}if len(order) != numCourses { return []int{} }return order}
1边从 p[1] 指向 p[0]:先修课指向后修课。入度加在后修课上。
2入度 0 的课先入队。主例一开始只有 0。
3出队立刻写入 order。主例三次出队后得到 [0,1,2],再收下 3。
4长度对不上就返回空切片。不要返回半截 order。
总结
Kahn 出队顺序就是修课序。主例 [0,1,2,3];写不满 numCourses 就返回 []。
- [a,b] 是 b→a,不是 a→b。图画反则入度全错。
- 1 和 2 谁先出队都合法,只要 0 在它们前、3 在它们后。
- 有环时队列会空、order 偏短。空数组是「修不完」,不是「没有先修」。