拓扑排序应用:拓扑排序是应用在有向图中,对所有的节点进行排序。
思路:
1.首先统计所有节点的入度,把其加入一个入度表中;
2.维护一个队列,讲入度为0的节点入队;并且维护一个邻接表用于存储每个节点相关联的节点入度数量;
3.然后将对列中的节点出队,并且将与这个节点相关联的节点入度减一。如果此时有相关的节点入度为0,则入 队。
4.如果最后都是入度为0的节点,则表示有拓扑排序;如果最后不存在入度为0的节点,则表示图有环,不存在拓扑排序。
如下图所示:(这个图借用了这篇博客:原文链接:https://blog.csdn.net/qq_41713256/article/details/80805338)
典型例题讲解
这个题目中的课程关联很容易联想到是拓扑排序,不过是方向相反,对于numCourses的判断,只要有节点出队的时候,让numCourses-1,最后判断numCourses是否为1即可
具体实现代码如下:
class Solution {
public boolean canFinish(int numCourses, int[][] prerequisites) {
int[] indegrees=new int[numCourses];//作为入度表
List<List<Integer>> adjacency=new ArrayList<>(); //邻接表
Queue<Integer> queue=new LinkedList<>();
for(int i=0;i<numCourses;i++){
adjacency.add(new ArrayList<>());
}
//注意理解,如果从题目的角度来说应该是这样:
// for(int[] c:prerequisites){
// indegrees[c[0]]++;
// adjacency.get(c[1]).add(c[0]);
// }
// 因为正常来说【1,2】,是1指向2,所以我们要对2入度,但是这里题目的意思的要完成1,要先完成2.
//不过我们这里还是采用正常的方向,因为对于判断有无环来说都是一样的,下面那个例题就不一样了
for(int[] c:prerequisites){
indegrees[c[1]]++;
adjacency.get(c[0]).add(c[1]);
}
for(int i=0;i<numCourses;i++){
if(indegrees[i]==0){
queue.add(i);
}
}
while(!queue.isEmpty()){
int num=queue.poll();
numCourses--;
for(int c:adjacency.get(num)){
if(--indegrees[c]==0){
queue.add(c);
}
}
}
return numCourses>0?false:true;
}
}
这个题目就需要打印出拓扑排序了,其实只需要对上面的代码加一个数组即可,当然这样不是最好解法,只是为了套用上面的模板。
class Solution {
public int[] findOrder(int numCourses, int[][] prerequisites) {
int[] res=new int[numCourses]; //添加一个数组,记录出队的课程
int[] indegrees=new int[numCourses];
List<List<Integer>> adjacency=new ArrayList<>();
Queue<Integer> queue=new LinkedList<>();
int cur=0;
for(int i=0;i<numCourses;i++){
adjacency.add(new ArrayList<>());
}
//这里就必须按照方向来了,否则会输入想法的顺序
for(int[] c:prerequisites){
indegrees[c[0]]++;
adjacency.get(c[1]).add(c[0]);
}
for(int i=0;i<numCourses;i++){
if(indegrees[i]==0){
queue.add(i);
}
}
while(!queue.isEmpty()){
int num=queue.poll();
numCourses--;
res[cur++]=num;
for(int c:adjacency.get(num)){
if(--indegrees[c]==0){
queue.add(c);
}
}
}
return numCourses==0?res:new int[0];
}
}
关于拓扑排序基本上都可以参考上述的模板。