拓扑排序

1. 什么是拓扑排序?

对于任何有向图而言,其拓扑排序为其所有结点的一个线性排序(对于同一个有向图而言可能存在多个这样的结点排序)。该排序满足这样的条件——对于图中的任意两个结点uv,若存在一条有向边从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;
    }
}
原文地址:https://www.cnblogs.com/zcxhaha/p/11409214.html