1. package treeAndRecursion.code207;
    2. import java.util.ArrayList;
    3. import java.util.LinkedList;
    4. import java.util.List;
    5. import java.util.Queue;
    6. /**
    7. * 经典的拓扑排序的问题
    8. * 知识点:
    9. * 1:无向图的度数,就是无向图中,和这个点相连的点的数量
    10. * 2:有向图的度数。入度:进入这个点的数量。出度:从这个点出去的数量。
    11. *
    12. * 引申到题目,什么课程(点)可以直接修呢?就是入度为0的点,可以直接修。
    13. * 所以要做拓扑排序,要先统计途中点的入度
    14. *
    15. */
    16. public class Solution {
    17. public boolean canFinish(int numCourses, int[][] prerequisites) {
    18. //1:初始化
    19. //1.1 初始化出边数组,就是初始化图,建立边的关系
    20. to = new ArrayList<List<Integer>>();
    21. for(int i = 0 ; i < numCourses ; i ++){
    22. to.add(i,new ArrayList<>());
    23. }
    24. // 1.2 初始化统计图的入度的数组
    25. inDeg = new int[numCourses];
    26. // 1.3 记录BFS中,修过了那些课程
    27. List<Integer> lesson = new ArrayList<>();
    28. for(int[] pre : prerequisites){
    29. //根据题意,第一个是ai第二个是bi
    30. int ai = pre[0];
    31. int bi = pre[1];
    32. //如果想修ai要先修bi的话,要从bi -> ai 建边
    33. to.get(bi).add(ai);
    34. //当加了一条边,是指向ai的,则入度统计里,ai + 1
    35. inDeg[ai] ++;
    36. }
    37. //2: 要执行BFS,广度优先遍历,要先入队
    38. Queue<Integer> queue = new LinkedList<>();
    39. //2.1 遍历所有入度统计数组,当时入度为0的,说明是可以直接修的
    40. // 拓扑排序的第一步:从0入度出发作为起点。
    41. for (int i = 0; i < numCourses; i ++){
    42. //如果入度为0,则放入队列
    43. if(inDeg[i] == 0){
    44. //入队就表示可以修了
    45. queue.add(i);
    46. }
    47. }
    48. //3: 执行BFS
    49. while (!queue.isEmpty()){
    50. // 3.1 x 出队,代表x 可以修了。然后记录x修过了
    51. int x = queue.poll();
    52. lesson.add(x);
    53. // 拓扑排序的第二步:扩展一个点,周围点的入度-1
    54. for(int y : to.get(x)){
    55. inDeg[y]--;
    56. // 拓扑排序的第三步:入度减为0,说明可以入队了
    57. //说明这门课程的全部先修课都被修完了
    58. if(inDeg[y] == 0){
    59. queue.add(y);
    60. }
    61. }
    62. }
    63. //4:返回值。如果修过的课程数 = 全部课程数,则返回true,否则false
    64. return lesson.size() == numCourses;
    65. }
    66. //使用出边数组构建图
    67. private List<List<Integer>> to;
    68. //统计图中点的入度的数组
    69. private int[] inDeg;
    70. }