1. 什么是拓扑排序?
对于任何有向图而言,其拓扑排序为其所有结点的一个线性排序(对于同一个有向图而言可能存在多个这样的结点排序)。该排序满足这样的条件——对于图中的任意两个结点u和v,若存在一条有向边从u指向v,则在拓扑排序中 u 一定出现在 v 前面。
拓扑排序主要用来解决有向图中的依赖解析(dependency resolution)问题。
2. 拓扑排序存在的条件?
当且仅当一个有向图为有向无环图(directed acyclic graph,或称DAG)时,才能得到对应于该图的拓扑排序。
3. 算法的实现
通过队列实现:
- 寻找入度为 0 的结点,将其放入队列中。
- 依次将队列中的点取出,并将该点所指向的邻接点的入度减 1
- 如果该节点的入度变为 0,则将其加入队列,否则不做任何操作
- 当队列为空的时候判断,是否每一个结点均被访问过。若均被访问过则出队序列则为即为符合条件的一个拓扑队列,否则不存在拓扑队列。
4. 代码
class Solution {
/**
* 邻接表结点
*/
public class Node{
public int degree;
public List<Integer> list;
public Node(){
this.degree = 0;
this.list = new ArrayList<>();
}
}
/**
* @param numCourses 表示结点的数目
* @param prerequisites 表示结点之间的关系
*/
public boolean canFinish(int numCourses, int[][] prerequisites) {
Node[] nodes = new Node[numCourses];
Queue<Node> que = new LinkedList<>();
int s = 0, e = 0, cnt = numCourses;
//初始化
for(int i = 0; i < numCourses; ++i){
nodes[i] = new Node();
}
//构建邻接表
for(int i = 0; i < prerequisites.length; ++i){
s = prerequisites[i][1];
e = prerequisites[i][0];
nodes[s].list.add(e);
nodes[e].degree++;
}
//拓扑排序
for(int i = 0; i < numCourses; ++i){
if(nodes[i].degree == 0){
que.add(nodes[i]);
cnt--;
}
}
while(!que.isEmpty()){
Node tnode = que.peek();
que.poll();
for(int i = 0; i < tnode.list.size(); ++i){
if(--nodes[tnode.list.get(i)].degree == 0){
que.add(nodes[tnode.list.get(i)]);
cnt--;
}
}
}
//判断
if(cnt != 0){
return false;
}
return true;
}
}