题目来源:LeetCode207:课程表
问题抽象: 给定课程总数 numCourses 和先修课程依赖列表 prerequisites(prerequisites[i] = [a_i, b_i] 表示学习课程 a_i 需先完成课程 b_i),要求判断 能否完成所有课程的学习(即依赖图是否存在拓扑排序),需满足以下核心需求:
-
依赖图定义:
- 节点:
numCourses门课程(编号0到numCourses-1); - 有向边:
b_i → a_i(b_i是a_i的先修课)。
- 节点:
-
判定条件:
- 有向无环图(DAG):依赖图 无环 时可完成所有课程;
- 存在环路:若图中存在循环依赖(如
A→B→A),则无法完成学习。
-
输入约束:
- 课程数
numCourses ∈ [0, 2000](0表示无课程); - 依赖关系数
|prerequisites| ∈ [0, 5000]; - 依赖对
[a_i, b_i]满足a_i ≠ b_i且无重复。
- 课程数
-
输出要求:返回布尔值
true(可完成所有课程)或false(因循环依赖无法完成)。 -
计算要求:
- 时间复杂度 O(n + e)(
n为课程数,e为依赖数); - 空间复杂度 O(n + e)(存储邻接表和入度数组)。
- 时间复杂度 O(n + e)(
-
关键策略:
- 拓扑排序(BFS):
- 构建邻接表和入度数组;
- 入度为
0的课程入队; - 出队课程并减少依赖课程的入度,入度归零则入队;
- 若出队课程数
= numCourses则无环(返回true);
- DFS 环检测:通过递归栈标记判断环路。
- 拓扑排序(BFS):
输入:整数 numCourses(课程总数); 二维数组 prerequisites(依赖列表)。
输出:布尔值(表示能否完成所有课程)。
解题思路
该问题本质是判断课程依赖图是否存在拓扑排序,等价于图中是否存在环。使用**拓扑排序(BFS)**算法解决:
-
构建有向图:
- 使用邻接表存储课程依赖关系,索引为课程编号。
- 对每个依赖关系
[ai, bi],添加边bi → ai(学习ai前需先学bi)。
-
统计入度:
- 记录每门课程的入度(即依赖的前置课程数量)。
-
初始化队列:
- 将所有入度为 0 的课程加入队列(可直接学习的课程)。
-
BFS 遍历:
- 从队列中取出课程,将其指向的所有课程入度减 1。
- 若某课程入度减为 0,则加入队列。
- 记录已学习的课程数量。
-
判断结果:
- 若已学习课程数等于总课程数,则说明无环,可完成所有课程;否则存在环,无法完成。
此方法时间复杂度为 O ( N + E ) O(N + E) O(N+E)( N N N 为课程数, E E E 为依赖边数),空间复杂度为 O ( N + E ) O(N + E) O(N+E),符合题目要求。
代码实现(Java版)🔥点击下载源码
class Solution {
public boolean canFinish(int numCourses, int[][] prerequisites) {
// 构建邻接表
List<List<Integer>> graph = new ArrayList<>();
for (int i = 0; i < numCourses; i++) {
graph.add(new ArrayList<>());
}
// 统计每门课程的入度
int[] indegree = new int[numCourses];
// 填充邻接表和入度数组
for (int[] edge : prerequisites) {
int from = edge[1];
int to = edge[0];
graph.get(from).add(to);
indegree[to]++;
}
// 初始化队列(BFS)
Queue<Integer> queue = new LinkedList<>();
for (int i = 0; i < numCourses; i++) {
if (indegree[i] == 0) {
queue.offer(i);
}
}
// 记录已学课程数量
int count = 0;
while (!queue.isEmpty()) {
int course = queue.poll();
count++;
// 遍历当前课程的后继课程
for (int next : graph.get(course)) {
indegree[next]--;
// 若后继课程入度为0,加入队列
if (indegree[next] == 0) {
queue.offer(next);
}
}
}
// 判断是否所有课程均可学习
return count == numCourses;
}
}
代码说明
-
邻接表构建:
- 使用
List<List<Integer>>存储图结构,索引对应课程编号。 - 遍历
prerequisites,为每条依赖关系添加边(bi → ai)。
- 使用
-
入度统计:
- 数组
indegree记录每门课程的入度(依赖的前置课程数量)。
- 数组
-
BFS 初始化:
- 将所有入度为 0 的课程加入队列(无前置依赖的课程)。
-
拓扑排序过程:
- 从队列中取出课程,计数加 1。
- 遍历其后继课程,将其入度减 1。若入度减为 0,则加入队列。
-
结果判定:
- 若最终计数等于总课程数,说明无环,返回
true;否则存在环,返回false。
- 若最终计数等于总课程数,说明无环,返回
提交详情(执行用时、内存消耗)

1461

被折叠的 条评论
为什么被折叠?



