207. 课程表(中等)
题目
你这个学期要修 numCourses 门课,编号从 0 到 numCourses - 1。有些课有先修课:数组 prerequisites 里的每一项 [a, b] 表示「想修 a 之前,必须先修完 b」。
请你判断:是否存在一种选课顺序,能把所有课都修完。
换一种说法:把每门课看成图上的一个点,把 b → a 看作一条有向边。如果能修完所有课,就等价于这张依赖图里没有环;一旦有环,就会出现「修 A 要等 B、修 B 又要等 A」的死锁。
自造示例 1:numCourses = 4,prerequisites = [[1,0],[2,1],[3,2]] → 可以按 0 → 1 → 2 → 3 修完,返回 true。
自造示例 2:numCourses = 3,prerequisites = [[0,1],[1,2],[2,0]] → 0 → 1 → 2 → 0 成环,任何一门都在等别的课,返回 false。
约束:1 ≤ numCourses ≤ 2000;0 ≤ prerequisites.length ≤ 5000;每项恰有 2 个整数;0 ≤ ai, bi < numCourses;所有课程对互不相同。
思路
一句话:把「能否修完」转化为「依赖有向图里有没有环」,再用拓扑排序检验。
先想最直白的做法:枚举所有可能的选课顺序,逐个检查是否满足每一条先修关系。课程最多 2000 门,排列数量是阶乘级,完全不可行。
退一步,用 DFS 沿着先修链往下走似乎可行;但如果只用一个「访问过」的 set,分不清「正在当前路径上」和「早已验证完成」,很容易把无环图误判成有环。
关键观察:一张依赖图没有环时,总能把所有点排成一个线性的先后顺序,这个顺序叫拓扑序。所以问题退化成:能不能把 numCourses 个点全部排进拓扑序。全排进去就是无环;排不完整,剩下的点必然构成环。
求拓扑序有两种常用姿势:
- Kahn 算法(BFS):不停修掉「当前没有任何未修先修课」的课程,直观、好写、不依赖递归。本文选这个。
- DFS 三色标记:
0未访问、1正在当前路径里、2已完成;DFS 时再遇到1,就是遇到了环。
选定 Kahn 后,把 [a, b] 画成边 b → a,并统计每门课的入度:还有几个先修没完成。入度为 0 的课现在就可以直接修。
解法
维护一张邻接表和一个入度数组,用队列不断处理入度为 0 的课:
- 初始化
graph与indegree;对每条[a, b]加边graph[b].push_back(a),同时indegree[a] += 1。 - 把当前所有
indegree == 0的课入队。 - 每从队头取出一门课,就代表它已修完,
done++;再遍历它的后继课,把后继入度减 1。 - 若某个后继入度减到 0,说明它的先修都已修完,把它也入队。
- 循环结束后,若
done == numCourses返回true;否则剩下没被处理的课形成环,返回false。
以自造示例 1 走一遍(边:[1,0] → 0→1、[2,1] → 1→2、[3,2] → 2→3):
| 轮次 | 队首课程 | 完成后 done | 处理后继 | 队列 |
|---|---|---|---|---|
| 1 | 0 | 1 | 1 的入度 1→0,入队 | [1] |
| 2 | 1 | 2 | 2 的入度 1→0,入队 | [2] |
| 3 | 2 | 3 | 3 的入度 1→0,入队 | [3本篇为付费内容您已阅读试读部分,购买后即可解锁全文。 ⚠️ 本站内容已做唯一性标记并受版权保护,未经授权禁止转载,侵权必究。 |
还没有评论,来说两句?