你这个学期必须选修?numCourses
?门课程,记为?0
?到?numCourses - 1
?。
在选修某些课程之前需要一些先修课程。 先修课程按数组?prerequisites
?给出,其中?prerequisites[i] = [ai, bi]
?,表示如果要学习课程?ai
?则?必须?先学习课程??bi
?。
[0, 1]
?表示:想要学习课程?0
?,你需要先完成课程?1
?。请你判断是否可能完成所有课程的学习?如果可以,返回?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 。这是不可能的
有向无环图:一定有一个点的入度为0,如果找不到入度为0的点,这个图一定是带环的
入度:入度就是指向该结点的边数
出度:出度就是该结点指向其他结点的边数
拓扑排序思路:一个有向无环图,如果图中存在入度为0 的节点,就把这个点删掉,同时也删掉这个点所连的边;重复上述步骤,如果所有点都能被删掉,则这个图可以进行拓扑排序
将本题建模成一个求拓扑排序的问题了:
算法
对于图中的任意一个节点,它在搜索的过程中有三种状态,即:
通过上述的三种状态,我们就可以给出使用深度优先搜索得到拓扑排序的算法流程,在每一轮的搜索搜索开始时,我们任取一个「未搜索」的节点开始进行深度优先搜索。
package matrix;
import java.util.ArrayList;
import java.util.List;
public class TuoPu {
public static void main(String[] args) {
int numCourses = 2;
int[][] prerequisites = {{1,0}};
System.out.println(canFinish(numCourses, prerequisites));
}
// 课程表
// 存储每个课程的邻接节点列表
static List<List<Integer>> edges;
// 存储每个课程是否被访问过
static int[] visited;
// 表示当前拓扑排序是否合法
static boolean vaild = true;
public static boolean canFinish(int numCourses, int[][] prerequisites) {
// 初始化邻接表和访问状态
edges = new ArrayList<List<Integer>>();
for(int i = 0; i < numCourses; i++){
edges.add(new ArrayList<Integer>());
}
visited = new int[numCourses];
for(int[] info : prerequisites){
edges.get(info[1]).add(info[0]);
}
// 进行拓扑排序
for(int i = 0; i < numCourses && vaild; i++){
if(visited[i] == 0){
dfsHelp(i); // 调用深度优先搜索进行拓扑排序
}
}
return vaild;
}
// 深度优先搜索 用于拓扑排序
public static void dfsHelp(int u){
// 标记当前节点为已访问
visited[u] = 1;
// 遍历当前节点的所有邻接节点
for(int v : edges.get(u)){
// 如果邻接节点未被访问递归
if(visited[v] == 0){
dfsHelp(v);
// 若存在环 则终止搜索
if(! vaild){
return;
}
}else if(visited[v] == 1){
// 若邻接节点正在被访问 说明存在环 终止访问
vaild = false;
return;
}
}
// 标记当前节点已经被完全访问
visited[u] = 2;
}
}
参考:
作者:力扣官方题解
链接:https://leetcode.cn/problems/course-schedule/solutions/359392/ke-cheng-biao-by-