题目描述
你这个学期必须选修 numCourses
门课程,记为 0
到 numCourses - 1
。
在选修某些课程之前需要一些先修课程。 先修课程按数组 prerequisites
给出,其中 prerequisites[i] = [ai, bi]
,表示如果要学习课程 ai
则 必须 先学习课程 bi
。
- 例如,先修课程对
[0, 1]
表示:想要学习课程0
,你需要先完成课程1
。
请你判断是否可能完成所有课程的学习?如果可以,返回 true
;否则,返回 false
。
示例 1:
输入: numCourses = 2, prerequisites = [[1,0]]
输出: true
解释: 总共有 2 门课程。学习课程 1 之前,你需要完成课程 0 。这是可能的。
示例 2:
输入: numCourses = 2, prerequisites = [[1,0],[0,1]]
输出: false
解释: 总共有 2 门课程。学习课程 1 之前,你需要先完成课程 0 ;并且学习课程 0 之前,你还应先完成课程 1 。这是不可能的。
提示:
1 <= numCourses <= 105
0 <= prerequisites.length <= 5000
prerequisites[i].length == 2
0 <= ai, bi < numCourses
prerequisites[i]
中的所有课程对 互不相同
解题思路
在本题中,我们可以将课程之间的关系抽象成图,每一门课程有几个选修课程,入度就是几。
那么没有依赖的先修课程的课程入度就是 0
,我们应该先学入度为 0
的课程,当整个课程学完的时候,需要把依赖于它的课程的入度 -1
,这样当某个课程的所有先修课程学完了,它的入度就变成了 0
,这其实就是图的拓扑排序的过程。
而那些存在互相为先修课程的课程对,会在图中形成环,它们的入度无法变成 0
,所以拓扑排序无法处理到它们。
所以我们可以将本题中的课程关系抽象成图进行拓扑排序,拓扑排序的过程就是学习课程的过程。在这个过程中,记录学完的课程的数量,当拓扑排序完成,就得到了可以学完的课程的数量。此时判断该数量是否等于 numCourses
,如果相等,则可以学完所有课程,否则没法学完所有课程。
动画演示
代码实现
var canFinish = function(numCourses, prerequisites) {
const inDegree = new Array(numCourses).fill(0);//入度数组
const map = {};//邻接表
// 遍历二维数组
for(let i =0; i < prerequisites.length; i++){
inDegree[prerequisites[i][0]]++;//求每一堂课的初始入度值
// 判断当前课已经存在邻接表
if(map[prerequisites[i][1]]){
// 添加依赖它的后续课
map[prerequisites[i][1]].push(prerequisites[i][0])
}else{
// 当前课不存在邻接表
map[prerequisites[i][1]] = [prerequisites[i][0]]
}
}
// 处理入度为0节点存到队列(先进先出)
const queue = [];
// 队列初始化
for(let i = 0; i < inDegree.length; i++){
if(inDegree[i] == 0) queue.push(i)
}
let cnt = 0;//当前选的课程
while(queue.length){
const selected = queue.shift();
cnt++;
const toEnQueue = map[selected];//当前课程的后续课程
if(toEnQueue && toEnQueue.length){
for(let i = 0; i<toEnQueue.length; i++){
inDegree[toEnQueue[i]]--;
if(inDegree[toEnQueue[i]] === 0){
queue.push(toEnQueue[i]);
}
}
}
}
return cnt == numCourses;//选修了所有的课程;
};
至此我们就完成了leetcode-207-课程表