package treeAndRecursion.code207;import java.util.ArrayList;import java.util.LinkedList;import java.util.List;import java.util.Queue;/** * 经典的拓扑排序的问题 * 知识点: * 1:无向图的度数,就是无向图中,和这个点相连的点的数量 * 2:有向图的度数。入度:进入这个点的数量。出度:从这个点出去的数量。 * * 引申到题目,什么课程(点)可以直接修呢?就是入度为0的点,可以直接修。 * 所以要做拓扑排序,要先统计途中点的入度 * */public class Solution { public boolean canFinish(int numCourses, int[][] prerequisites) { //1:初始化 //1.1 初始化出边数组,就是初始化图,建立边的关系 to = new ArrayList<List<Integer>>(); for(int i = 0 ; i < numCourses ; i ++){ to.add(i,new ArrayList<>()); } // 1.2 初始化统计图的入度的数组 inDeg = new int[numCourses]; // 1.3 记录BFS中,修过了那些课程 List<Integer> lesson = new ArrayList<>(); for(int[] pre : prerequisites){ //根据题意,第一个是ai第二个是bi int ai = pre[0]; int bi = pre[1]; //如果想修ai要先修bi的话,要从bi -> ai 建边 to.get(bi).add(ai); //当加了一条边,是指向ai的,则入度统计里,ai + 1 inDeg[ai] ++; } //2: 要执行BFS,广度优先遍历,要先入队 Queue<Integer> queue = new LinkedList<>(); //2.1 遍历所有入度统计数组,当时入度为0的,说明是可以直接修的 // 拓扑排序的第一步:从0入度出发作为起点。 for (int i = 0; i < numCourses; i ++){ //如果入度为0,则放入队列 if(inDeg[i] == 0){ //入队就表示可以修了 queue.add(i); } } //3: 执行BFS while (!queue.isEmpty()){ // 3.1 x 出队,代表x 可以修了。然后记录x修过了 int x = queue.poll(); lesson.add(x); // 拓扑排序的第二步:扩展一个点,周围点的入度-1 for(int y : to.get(x)){ inDeg[y]--; // 拓扑排序的第三步:入度减为0,说明可以入队了 //说明这门课程的全部先修课都被修完了 if(inDeg[y] == 0){ queue.add(y); } } } //4:返回值。如果修过的课程数 = 全部课程数,则返回true,否则false return lesson.size() == numCourses; } //使用出边数组构建图 private List<List<Integer>> to; //统计图中点的入度的数组 private int[] inDeg;}