面试知识库

207. 课程表#

今天要聊的这道题,困扰了无数求职者。但只要掌握了正确的思维方法,你会发现它其实很优雅。

🎓 从选课系统说起#

小明最近在给弟弟规划大学课程,发现了一个有趣的问题:

  • “要学高数2,得先学高数1”
  • “要学数据结构,得先学C语言”
  • “要学操作系统,得先学数据结构”

这不就是今天要解决的算法题吗?

问题描述#

题目目标#

你这个学期必须选修 numCourses 门课程,课程编号为 0 到 numCourses - 1 。给定一个数组 prerequisites ,其中 prerequisites[i] = [ai, bi] 表示如果要学习课程 ai 就必须先完成课程 bi 。请判断是否可能完成所有课程。

示例 1#

输入: numCourses = 2, prerequisites = [1,0](#) 输出: true 依赖关系示意:

0 -> 1
text

说明: 把输入和输出放在一起看,会更容易直接抓住题目要求的变化。

补充说明#

  • prerequisites[i] = [a, b] 表示必须先学课程 b,才能学习课程 a。

💡 问题的本质#

LeetCode 207题”课程表”的描述是这样的:

你总共需要修完 numCourses 门课程
prerequisites[i] = [ai, bi] 表示:想要学习课程 ai ,必须先完成课程 bi

判断是否可能完成所有课程的学习?

示例:
输入:numCourses = 4, prerequisites = [[1,0],[2,1],[3,2]]
输出:true
解释:可以按照 0→1→2→3 的顺序学习
plaintext

🤔 这题的关键是什么?#

本质上,我们在判断:课程之间的依赖关系是否形成了”环”。

  • 如果有环:比如 A依赖B、B依赖C、C依赖A,那就不可能完成
  • 如果无环:就一定存在一种可行的学习顺序

这就是典型的拓扑排序问题!

🎬 模拟运行:看看算法是如何工作的#

让我们用一个具体例子,一步步看清算法的运行过程:

⚡ 代码实现:BFS解法#

🎯 算法要点解析#

拓扑排序的精髓在于:

  1. 找到所有没有依赖的节点(入度为0)
  2. 删除这些节点,并更新其他节点的依赖状态
  3. 重复以上步骤,直到:
    • 所有节点都被删除(有解)
    • 或剩下的节点都有依赖(无解)

📊 复杂度分析#

时间复杂度:O(N + E)

  • N是课程数量
  • E是依赖关系的数量

空间复杂度:O(N + E)

  • 邻接表和队列的空间开销

🎯 面试官最爱追问#

  1. Q:如何输出一个可行的学习顺序? A:只需要将BFS过程中的课程号按顺序记录下来

  2. Q:能不能用DFS解决? A:可以,通过检测环的方式实现

  3. Q:如果有多个可行解,如何输出字典序最小的解? A:将队列改为优先队列,按课程号排序

💡 举一反三#

这个模板还可以解决:

  • 任务调度
  • 软件包依赖管理
  • 项目构建顺序
  • 施工工序安排

🎁 思考题#

如果每门课程都有一个学习时长,求完成所有课程的最短时间?

例如:
课程时长:[3,2,4,1](小时)
依赖关系:[[1,0],[2,1],[3,2]]
plaintext

如果你知道答案,或者有自己的想法?欢迎在评论区留言、讨论~

📝 代码模板总结#

拓扑排序的通用步骤:

  1. 统计入度
  2. 将入度为0的节点入队
  3. BFS删除节点并更新入度
  4. 判断是否所有节点都被删除