这道题的本质是判断:是否可以完成所有课程,即判断图中是否存在环。
换句话说:从图中的某个节点出发,是否能够遍历完整个有向图,不形成环。
为了解决这个问题,我们可以使用拓扑排序(Kahn’s Algorithm) 。
思路如下:
- 使用一个邻接表
graph表示课程之间的依赖关系; - 使用一个入度数组
indegree统计每门课程的前置课程数量; - 将所有入度为 0 的课程加入队列(这些课程没有依赖,可以先学);
- 每学习一门课程,就将其邻接课程的入度减 1;
- 如果某门课程的入度变为 0,说明可以开始学习,继续加入队列;
- 最终如果遍历过的课程数等于总课程数,说明可以完成所有课程;否则图中存在环,无法完成。
var canFinish = function (numCourses, prerequisites) {
let graph = new Map();
let indegree = new Array(numCourses).fill(0);
// 构建邻接表以及入度统计数组
for (let [after, pre] of prerequisites) {
indegree[after]++;
if (!graph.has(pre)) graph.set(pre, []);
graph.get(pre).push(after);
}
// indegree = 0
let queue = [];
for (let i = 0; i < numCourses; ++i) {
if (indegree[i] === 0) queue.push(i);
}
let cnt = 0;
while (queue.length) {
let current = queue.shift();
cnt++;
// 存在邻接表的课程(学完current之后,还有得学
if (graph.get(current)) {
for (let next of graph.get(current)) {
// 减少current课程所链接的下一门课程的入度
indegree[next]--;
// 入度为0,表示可以从这门课程入手了
if (indegree[next] === 0) {
queue.push(next);
}
}
}
}
return cnt === numCourses;
};
Tips:
- 出现环的标志就是队列无法消除所有课程的入度,导致某些课程永远入度不为 0。
- 例如依赖关系
[5, 5]会导致课程 5 依赖自己,产生自环。