Course Schedule (M)
There are a total of n courses you have to take, labeled from 0
to n-1
.
Some courses may have prerequisites, for example to take course 0 you have to first take course 1, which is expressed as a pair: [0,1]
Given the total number of courses and a list of prerequisite pairs, is it possible for you to finish all courses?
Example 1:
Input: 2, [[1,0]]
Output: true
Explanation: There are a total of 2 courses to take.
To take course 1 you should have finished course 0. So it is possible.
Example 2:
Input: 2, [[1,0],[0,1]]
Output: false
Explanation: There are a total of 2 courses to take.
To take course 1 you should have finished course 0, and to take course 0 you should
also have finished course 1. So it is impossible.
Note:
- The input prerequisites is a graph represented by a list of edges, not adjacency matrices. Read more about how a graph is represented.
- You may assume that there are no duplicate edges in the input prerequisites.
题意
假设选课时有一些课程有前置课程要求,给定一系列课程及其所有的前置课程,判断能否上完所有课。
思路
很典型的拓扑排序问题,经典解法有 BFS 和 DFS 两种。当图中存在环时则没有办法上完所有课。
代码实现 - BFS
class Solution {
public boolean canFinish(int numCourses, int[][] prerequisites) {
int[] inDegree = new int[numCourses]; // 入度数组
List<List<Integer>> edge = new ArrayList<>(); // 记录图信息
// 初始化邻接表
for (int i = 0; i < numCourses; i++) {
edge.add(new ArrayList<>());
}
// 生成图并记录初始入度信息
for (int i = 0; i < prerequisites.length; i++) {
inDegree[prerequisites[i][0]]++;
edge.get(prerequisites[i][1]).add(prerequisites[i][0]);
}
// 将所有入度为0的结点入队
Queue<Integer> q = new ArrayDeque<>();
for (int i = 0; i < numCourses; i++) {
if (inDegree[i] == 0) {
q.offer(i);
}
}
// 更新结点入度和队列,并记录拓扑序列中结点的个数
int count = 0;
while (!q.isEmpty()) {
int u = q.poll();
count++;
for (int i = 0; i < edge.get(u).size(); i++) {
int v = (int) edge.get(u).get(i);
inDegree[v]--;
if (inDegree[v] == 0) {
q.offer(v);
}
}
}
// 如果有结点不在拓扑序列中,说明图存在环
return count == numCourses;
}
}
代码实现 - DFS
// DFS方法实际上就是判断是否为有向无环图
class Solution {
private boolean[] visit; // 记录结点是否被访问
private boolean[] inStack; // 记录结点是否还在递归栈中
private List<List<Integer>> edge; // 记录图
public boolean canFinish(int numCourses, int[][] prerequisites) {
visit = new boolean[numCourses];
inStack = new boolean[numCourses];
edge = new ArrayList<>();
// 初始化邻接表
for (int i = 0; i < numCourses; i++) {
edge.add(new ArrayList<>());
}
// 生成图
for (int i = 0; i < prerequisites.length; i++) {
edge.get(prerequisites[i][1]).add(prerequisites[i][0]);
}
// DFS处理
for (int i = 0; i < numCourses; i++) {
if (!visit[i]) {
boolean flag = dfs(i);
if (!flag) return false; // 存在环则不可能有拓扑序列
}
}
return true;
}
private boolean dfs(int u) {
visit[u] = true;
inStack[u] = true;
for (int v : edge.get(u)) {
if (!visit[v]) {
boolean flag = dfs(v);
if (!flag) return false;
} else if (inStack[v]) {
return false; // 如果下一条边连接的结点还在递归栈中,说明存在环
}
}
inStack[u] = false; // 所有出边处理完后,将当前结点排除在递归栈外
return true;
}
}