持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第2天,点击查看活动详情
题目描述
题目释义
-
课程总数为n,编号从0,n-1;
-
依赖条件二维数组,每个数组代表一个依赖条件,即a1->a0; 这个是个典型的拓扑排序,什么是拓扑排序呢?对一个有向无环图(Directed Acyclic Graph简称DAG)G进行拓扑排序,是将G中所有顶点排成一个线性序列,使得图中任意一对顶点u和v,若边<u,v>∈E(G),则u在线性序列中出现在v之前。通常,这样的线性序列称为满足拓扑次序(Topological Order)的序列,简称拓扑序列。 简单来说就是图中的各个顶点之间都是有向的,比如A->B,则意味着A在B之前。也可以由此延伸出入度和出度的概念。入度是指向本顶点的箭头数,出度是指从本顶点向外发出的箭头数。 拓扑排序就是把满足这样条件的图,从入度为0的顶点A开始,删除从A发出的箭头,即将B、D入度在原来的基础上减1。然后循环。依次将入度变为0的顶点进行排序,就得到了拓扑排序。
如图是一个有向无环图,它的拓扑排序应该是A->D->B->F
解题思路
反观这个题,题解思路就很清晰明了了。其中重要的数据处理就是要计算每个顶点的入度,然后从入度为0的开始消除箭头。循环往复,最后如果循环次数不等于课程数,直接返回false。 那接下来就一起来coding吧!
代码实现
public static boolean canFinish(int numCourses, int[][] prerequisites) {
//使用数组来装所有顶点的连接顶点
//角标为课程num,角标对应的数组为该课程对应的上级连接课程,换句话说,这个数组的size就是入度数
List<List<Integer>> inInfoList = new ArrayList<>(numCourses);
//初始化这个数组
for (int i =0;i<numCourses;i++){
inInfoList.add(new ArrayList<>());
}
//二维数组转成上述list,因为依赖关系是prerequisite[1]->prerequisite[0],所以数组中的第二个元素是第一个元素的上级连接课程
for (int[] prerequisite : prerequisites) {
inInfoList.get(prerequisite[0]).add(prerequisite[1]);
}
//用栈存储入度为0的课程num
Stack<Integer> passIndex = new Stack<>();
for (int i=0;i<inInfoList.size();i++){
if (inInfoList.isEmpty()){
passIndex.add(i);
}
}
//循环计数器
int count = 0;
//循环截止条件:栈中不存在入度为0的课程num了
while (!passIndex.isEmpty()){
//弹出栈-栈顶入度为0的课程num
Integer currentNum = passIndex.pop();
//循环次数加1
count ++;
for (int i=0;i<inInfoList.size();i++){
List<Integer> integers = inInfoList.get(i);
//如果连接课程为空,说明之前已经放入过了栈中,直接continue
if (integers.size() == 0){
continue;
}
//将当前课程从上级连接课程中去掉
integers.remove(currentNum);
//如果剩余连接课程size为0,则直接放入栈中
if (integers.size() == 0){
passIndex.push(i);
}
}
}
//如果循环次数=课程数,则表示所有课程都满足了入度为0的条件,返回true,否则为false
return count == numCourses;
}