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; } }