1. 课程表问题解析
课程表问题(Course Schedule)是算法领域经典的拓扑排序应用场景,也是力扣(LeetCode)平台上高频出现的面试题型。这个问题抽象自大学选课场景:假设你需要修读n门课程,某些课程可能有先修要求(比如必须先修完数学才能学习物理),如何判断能否顺利完成所有课程?
这个问题的核心在于检测有向图中是否存在环。如果课程依赖关系形成环(比如A依赖B,B依赖C,C又依赖A),那么这些课程将永远无法完成。拓扑排序算法能够有效解决这类依赖关系检测问题。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 问题建模与算法选择
2.1 问题建模
我们可以将课程表问题建模为有向图:
- 节点:每门课程(编号0到n-1)
- 边:如果课程u是课程v的先修课,则添加一条从u指向v的边
例如,输入prerequisites = [[1,0]]表示课程1需要先修课程0,对应的图就是0→1。
2.2 算法选择
解决这类依赖关系问题,主要有两种经典算法:
-
Kahn算法(基于入度统计):
- 统计每个节点的入度(有多少边指向它)
- 将入度为0的节点加入队列
- 依次处理队列中的节点,将其邻接节点的入度减1
- 如果最终所有节点都被处理,说明无环;否则存在环
-
DFS深度优先搜索:
- 对每个未访问的节点启动DFS
- 维护三种状态:未访问、访问中、已访问
- 如果在DFS过程中遇到"访问中"的节点,说明存在环
两种算法的时间复杂度都是O(V+E)(V是节点数,E是边数)。Kahn算法通常更直观,而DFS可能在某些情况下更节省空间。
3. Kahn算法实现详解
3.1 算法步骤
以下是Kahn算法的完整实现步骤:
-
构建邻接表和入度数组:
- 邻接表:记录每个节点的后继节点
- 入度数组:记录每个节点的入度数
-
初始化队列:
- 将所有入度为0的节点加入队列
-
拓扑排序过程:
- 从队列取出节点,加入结果集
- 将该节点的所有邻接节点入度减1
- 如果邻接节点入度变为0,加入队列
-
结果验证:
- 如果结果集大小等于课程总数,说明可以完成所有课程
- 否则说明存在环,无法完成
