1. 具体题目
现在你总共有 n 门课需要选,记为 0 到 n-1。在选修某些课程之前需要一些先修课程。 例如,想要学习课程 0 ,你需要先完成课程 1 ,我们用一个匹配来表示他们: [0,1]。给定课程总量以及它们的先决条件,判断是否可能完成所有课程的学习?
示例 1: 输入: 2, [[1,0]] 输出: true 解释: 总共有 2 门课程。学习课程 1 之前,你需要完成课程 0。所以这是可能的。
说明: 输入的先决条件是由边缘列表表示的图形,而不是邻接矩阵,可以假定输入的先决条件中没有重复的边。
2. 思路分析
本题可套用有向图的拓扑排序算法——在构造一个有向图的拓扑序列时,若所有顶点都被输出过,那么说明该图不存在环,否则该图中有环。
有效的课程表中不应存在环路,所以要求构造过程中,所有顶点都被输出过。
边缘列表存储图的形式见 https://visualgo.net/en/graphds,例如顶点 V0 指向顶点 V1 表示为 prerequisites[i][1] -> prerequisites[i][0]
3. 代码
1 public boolean canFinish(int numCourses, int[][] prerequisites) { 2 int[] in = new int[numCourses]; 3 for(int[] nums : prerequisites){ 4 in[nums[0]]++; //统计各顶点入度 5 } 6 Stack<Integer> stack = new Stack<>(); //记录入度为 0 的顶点 7 for(int i = 0; i < numCourses; i++){ 8 if(in[i] == 0){ 9 stack.push(i); 10 } 11 } 12 while(!stack.empty()){ 13 int course = stack.pop(); 14 numCourses--; 15 for(int[] nums : prerequisites){ 16 if(nums[1] == course){ //将当前顶点连接的所有顶点的入度 -1 17 if(--in[nums[0]] == 0) stack.push(nums[0]); //若-1后入度为0则进栈 18 } 19 } 20 } 21 return numCourses == 0; 22 }