一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第1天,点击查看活动详情。
一.题目:
207. 课程表 你这个学期必须选修
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 <= 1050 <= prerequisites.length <= 5000prerequisites[i].length == 20 <= ai, bi < numCoursesprerequisites[i]中的所有课程对 互不相同
二、思路分析:
这道题目是个中等难度的题目,题目中的先修课程说明了我们需要先学习指定的课程才能继续后面的课程,即有条件的,而且可能是多种条件的,所以我们就需要利用到图的思路,如果产生了循环闭路图,说明题目的要求我们就不能够做到返回false,所以对于这种题我们采取拓扑排序的思路。
- 统计
入度的数组保证有需要先修的课程 - 构建对应的
邻接表,键为先修课程编号,对应的值为需要这个先修课程才能学的课程编号 - 随后创建一个队列保存入度为
0的课程 - 逐渐将入度为
0的课程出队,那么可以影响统计入度的数组通过邻接表来更新数组,如果在发现入度为0的课程继续入队循环操作 - 最后返回看看
出队的总数是否与课程数相同即可完成作答
三、代码:
/**
* @param {number} numCourses
* @param {number[][]} prerequisites
* @return {boolean}
*/
var canFinish = function (numCourses, prerequisites) {
//统计入度数组
let indegree = new Array(numCourses).fill(0)
//构建邻接表,key:课程标号 value:对应后续表
let coursemap = {}
for (const precourse of prerequisites) {
indegree[precourse[0]]++
if (coursemap[precourse[1]]) {
coursemap[precourse[1]].push(precourse[0])
} else {
coursemap[precourse[1]] = [precourse[0]]
}
}
//创建一个队列保存入度为0的队列
let queue = []
for (let i = 0; i < indegree.length; i++) {
if (indegree[i] == 0){
queue.push(i)
}
}
//出队列的时候统计满足的课程数,然后更新一下邻接表的数据
let count = 0
while (queue.length) {
let popqueue = queue.shift()
count++
let respondarr = coursemap[popqueue]
//有后续课
if (respondarr && respondarr.length) {
for (let i = 0; i < respondarr.length; i++) {
indegree[respondarr[i]]--
if (indegree[respondarr[i]] == 0) {
queue.push(respondarr[i])
}
}
}
}
return count == numCourses
};
四、总结:
这道题目主要用到了拓扑排序的思路,通过题目给出的信息构建邻接表来更新关于课程的数组最后完成作答,思路很难想到但是实现起来还是比较容易的。