「这是我参与2022首次更文挑战的第12天,活动详情查看:2022首次更文挑战」
你这个学期必须选修 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]中的所有课程对 互不相同
解题思路
在本题中,我们可以将课程之间的关系抽象成图,每一门课程有几个选修课程,入度就是几。
那么没有依赖的先修课程的课程入度就是 0,我们应该先学入度为 0 的课程,当整个课程学完的时候,需要把依赖于它的课程的入度 -1,这样当某个课程的所有先修课程学完了,它的入度就变成了 0,这其实就是图的拓扑排序的过程。
如果你对拓扑排序还不了解的话,可以看我之前的你真的懂排序吗?这篇文章,这里我们不再重复讲了。
而那些存在互相为先修课程的课程对,会在图中形成环,它们的入度无法变成 0,所以拓扑排序无法处理到它们。
所以我们可以将本题中的课程关系抽象成图进行拓扑排序,拓扑排序的过程就是学习课程的过程。在这个过程中,记录学完的课程的数量,当拓扑排序完成,就得到了可以学完的课程的数量。此时判断该数量是否等于 numCourses,如果相等,则可以学完所有课程,否则没法学完所有课程。
动画演示
代码实现
var canFinish = function (numCourses, prerequisites) {
// 初始化 nums 数组记录每个课程的入度数量
const nums = Array(numCourses).fill(0),
// 初始化 map 数组记录课程间的依赖关系
map = Array(numCourses)
// 遍历输入数组,记录入度数量和课程间的依赖关系
for (let i = 0; i < prerequisites.length; i++) {
const [a, b] = prerequisites[i]
nums[a]++
if (map[b]) map[b].push(a)
else map[b] = [a]
}
// 初始化可以学完的课程数量为 0
let count = 0
// 初始化中间队列为空数组
const queue = []
// 遍历 nums 数组,将入度为 0 的课程首先放入队列
for (let i = 0; i < numCourses; i++) {
if (nums[i] === 0) queue.push(i)
}
// 当队列不为空的时候
while (queue.length) {
// 弹出队首的元素
const cur = queue.shift(),
// 获取依赖当前课程的课程列表
list = map[cur];
// 可以学完的课程数量 +1
count++
// 如果有依赖当前课程的课程,将它们的入度 -1
if (list) {
for (let i = 0; i < list.length; i++) {
nums[list[i]]--
// 如果某个课程的入度为 0,将其放入队列
if (nums[list[i]] === 0) queue.push(list[i])
}
}
}
// 返回可以修完的课程数量是否等于课程中数量
return count === numCourses
}
至此我们就完成了 leetcode-207-课程表
如有任何问题或建议,欢迎留言讨论!👏🏻👏🏻👏🏻