210. 课程表 II - Java 拓扑排序 Kahn算法
原题地址:https://leetcode.cn/problems/course-schedule-ii/
题解 这题和207. 课程表 都是拓扑排序,只是207中要求返回是否能够完成全部事件,而本题要求返回完成事件的顺序
对于一系列给定的点和边,如果有有向边A->B,则称A是B的依赖,完成事件时需要先完成A才能继续进行B,求能够完成所有事件的事件顺序叫做拓扑排序
不管是DFS还是Kahn算法,拓扑排序的原理都是相通的:将集合V中入度为0的点 全部转移至一个集合S,每次加入集合S时将该点所有的出度边 全部删除,然后检查删除边后是否有新的入度为0的点,如果有继续重复上述步骤直至无法继续更新
对于是否能够完成所有事件,排序后检查所有点入度是否均为0即可;对于拓扑排序得到的序列,在每次将点加入集合S时再将其加入一个List维护即可
Kahn算法 :
Kahn算法是一种基于BFS的拓扑排序,其中BFS的队列即为这里的集合S,用一个出度表out记录下每个点指向的点,再用一个入读表in记录每个点的入度
我们先将所有入度为0的点全部加入集合S,每次取出点时遍历其out表,将指向的所有点入度-1即为删除这两点间的有向边,每次更新完入度后检查该被指向点 入度是否为0,如果为0则加入队列中
由于本体要求返回事件顺序,每次有点加入集合S时将其也加入一个List/数组即可,最后返回该数组
如果遍历in检查到有非0入度的点则手动返回一个空数组
时间复杂度 :O(E+V)
空间复杂度 :O(E+V)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 class Solution { public int [] findOrder(int numCourses, int [][] prerequisites) { List<List<Integer>> out=new ArrayList <>(); int [] in=new int [100001 ]; int [] ans=new int [numCourses]; int ansItr=0 ; Queue<Integer> Kahn=new LinkedList <>(); for (int i=0 ;i<numCourses;i++){ out.add(new LinkedList <>()); } for (int i=0 ;i<prerequisites.length;i++){ out.get(prerequisites[i][1 ]).add(prerequisites[i][0 ]); in[prerequisites[i][0 ]]++; } for (int i=0 ;i<numCourses;i++){ if (in[i]==0 ){ Kahn.add(i); ans[ansItr++]=i; } } int course; while (!Kahn.isEmpty()){ course=Kahn.remove(); for (int i=0 ;i<out.get(course).size();i++){ in[out.get(course).get(i)]--; if (in[out.get(course).get(i)]==0 ){ Kahn.add(out.get(course).get(i)); ans[ansItr++]=out.get(course).get(i); } } } for (int i:in){ if (i!=0 )return new int [0 ]; } return ans; } }