关注
文末的名片达文汐
,回复关键词“力扣源码”,即可获取完整源码!!详见:源码和核心代码的区别
题目详情
你这个学期必须选修 numCourses
门课程,记为 0
到 numCourses - 1
。
在选修某些课程之前需要一些先修课程。先修课程按数组 prerequisites
给出,其中 prerequisites[i] = [ai, bi]
,表示如果要学习课程 ai
则必须先学习课程 bi
。
请你判断是否可能完成所有课程的学习?如果可以,返回 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 <= 2000
0 <= prerequisites.length <= 5000
prerequisites[i].length == 2
0 <= ai, bi < numCourses
prerequisites[i]
中的所有课程对互不相同
解题思路
该问题本质是判断课程依赖图是否存在拓扑排序,等价于图中是否存在环。使用**拓扑排序(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
。
- 若最终计数等于总课程数,说明无环,返回