题目描述
AC代码
题目需要解决的问题是:给定课程总量以及它们的先决条件,判断能不能学完这些课。
啥时候这个排课表没法进行呢,如果学课程A之前必须学B,学课程B之前必须学A,那这样的话课表是不可能拍出来的,所以这个问题可以转化为看看存不存在一个环的问题。学课程B之前必须学A,这样的关系有一个前驱有一个后继,我们可以认为是有一个方向存在的,如果这个课表可以被排出来,那么各门课程应该可以组成一个有向无环图,判断一个有向无环图是否有环的方法就是用拓扑排序来解决。
🌙之前我们学习到拓扑排序的工作流程如下:
-
在有向图中选一个没有前驱的顶点且输出它
-
从图中删除该顶点和所有以他为尾的弧
-
重复上述两步,直到全部顶点都输出或者图中不存在无前驱的顶点为止。
这样得到的序列就是一个拓扑序列。
那么这道题,我们就可以BFS去做这个拓扑排序。
算法流程:
- 统计课程安排图中每个节点的入度情况,保存在degree数组中。将课程之前的依赖关系通过set保存,借助一个队列q,将所有入度为0的节点入队,入度为0说明学习该门课程不需要其它先决条件。
- 当 queue 非空时,依次将队首节点cur出队,然后查找set集合,将其对应的邻接节点next的入度情况-1,也就是
degree[next]--
。如果它的入度情况为0了,此时该节点没有前驱,也就是说学习该门课程之前不需要再学习其他课程了,所以这门课可以入队了。 - 每一次我们都会用
cnt
变量去保存节点的个数,如果这个课表无法成功也就是存在环的话,那么我们记录的节点个数肯定和课程数目不相同,如果相同就说明可以排课。
class Solution {
public boolean canFinish(int n, int[][] array) {
HashSet<Integer>[] set = new HashSet[n];
int[] degree=new int[n]; //记录入度
for (int i = 0; i < n; i++) {
set[i] = new HashSet<>();
}
for(int[] x:array){
set[x[1]].add(x[0]);
degree[x[0]]++;
}
int cnt=0;
Queue<Integer> q=new LinkedList<>();
//从入度为0的节点,也就是没有前驱的节点开始拓扑排序
for(int i=0;i<n;i++)
if(degree[i]==0)
q.add(i);
while(!q.isEmpty()){
int cur=q.peek();
q.poll();
cnt++;
for(int next:set[cur]){
degree[next]--;
if(degree[next]==0)
q.add(next);
}
}
return cnt==n;
}
}