精华内容
下载资源
问答
  • 拓扑排序有向无环图
    2022-04-13 19:05:50

    基本概念:


    一个无环的有向图称为 有向无环图(Directed Acycline Graph,DAG)。
    有向无环图是描述一个工程、计划、生产、系统等流程的有效工具。一个大工程可分为 若干个子工程(活动),活动之间通常有一定的约束,例如先做什么活动、后做什么活动。
    用顶点表示活动,用弧表示活动之间的优先关系的有向图,称为顶点表示活动的网 (Activity On Vertex Network),简称 AOV 网。
    拓扑排序 是指将 AOV 网中的顶点排成一个线性序列,该序列必须满足:若从顶点 i 到顶点 j 有一条路径,则该序列中 顶点 i 一定在顶点 j 之前。

    拓扑排序的基本思想如下:

    1)选择一个无前驱的顶点并输出。

    2)从图中删除该顶点和该顶点的所有发出边。

    3)重复第 1 步和第 2 步,直到不存在无前驱的顶点。

    4)如果输出的顶点数小于 AOV 网中的顶点数,则说明网中有环,否则输出的序列即拓扑序列。

    需要注意的是,拓扑排序并不是唯一的,当存在多个无前驱的顶点时,可以选择其中任意一个输出。

    上述的描述过程中有删除顶点和边的操作,实际上,完全没必要真的删除顶点和边。可 以将没有前驱的顶点(入度为 0)暂存到栈中,输出时出栈即表示删除。边的删除只需要将 其邻接点的入度减 1 即可。

    拓扑排序的算法步骤如下:

    1)求出各顶点的入度,存入数组 indegree[]中,并将入度为 0 的顶点入栈 S。

    2)如果栈不空,则重复执行以下操作:

            • 栈顶元素 i 出栈,并保存到拓扑序列数组 topo[]中;

            • 顶点 i 的所有邻接点入度减 1,如果减 1 后入度为 0,立即入栈 S。

    3)如果输出的顶点数小于 AOV 网中的顶点数,则说明网中有环,否则输出拓扑序列。

    算法复杂度分析

    (1)时间复杂度 求有向图中各顶点的入度需要遍历邻接表,算法的时间复杂度为 O(e)。度数为 0 的顶点 入栈的时间复杂度为 O(n),若有向图无环,每个顶点出栈后其邻接点入度减 1,时间复杂度为 O(e)。总的时间复杂度为 O(n+e)。

    (2)空间复杂度 算法所需要的辅助空间包含入度数组 indegree[]、拓扑序列数组 topo[]、栈 S,则算法的 空间复杂度是 O(n)。

    参考:《趣学数据结构》作者:陈小玉

    图有很多种存储方法,其中,邻接矩阵和邻接表是两种简单且常用的方法。

    在进行拓扑排序时,因为删除顶点 i 时,要将顶点 i 的所有邻接点入度减 1,访问一个顶点的所有邻接点, 使用邻接表存储需要时间复杂度为该顶点的度,邻接矩阵需要 O(n),因此这里采用邻接表存储。

    有向图邻接表求出度容易,而逆邻接表求入度容易。

    package com.ymzh.demo4IoC;
    
    import java.util.*;
    
    public class Util4DAG {
        private static class Reference {
            private final Integer bean;
            private final Integer ref;
            
            public Reference (Integer bean, Integer ref) {
                this.bean = bean;
                this.ref = ref;
            }
    
            public Integer getBean() {
                return bean;
            }
    
            public Integer getRef() {
                return ref;
            }
        }
        /**
         * 根据边集合生成图的邻接表,并计算入度
         * @param references    边集合
         * @param graph         图的邻接表
         * @param inDegree      图的入度
         */
        public static void CreateGraphAndInDegree(List<Reference> references, Map<Integer, List<Integer>> graph, Map<Integer, Integer> inDegree){
            // 初始化 邻接表 和 入度
            for (Reference reference : references) {
                if (!graph.containsKey(reference.getBean())) {
                    graph.put(reference.getBean(), new ArrayList<>());
                    inDegree.put(reference.getBean(), 0);
                }
            }
            // 创建邻接表, 并计算入度
            for (Reference reference : references) {
                if(reference.getRef() != null) {
                    graph.get(reference.getRef()).add(reference.getBean());
                    inDegree.put(reference.getBean(), inDegree.get(reference.getBean())+1);
                }
            }
        }
    
        /**
         *
         * @param graph         图的邻接表
         * @param inDegree      图的入度
         * @param topo          图的拓扑排序
         * @return              是否存在拓扑排序
         * 若 G 无回路,则生成 G 的一个拓扑序列 topo[],并返回 true,否则 false
         */
        public static boolean TopologicalSort(Map<Integer, List<Integer>> graph, Map<Integer, Integer> inDegree, List topo) {
            // 辅助栈
            Deque<Integer> stack = new ArrayDeque<>();
            //入度为 0 者进栈
            for(Integer key : graph.keySet()) {
                if(inDegree.get(key) == 0){
                    stack.addLast(key);
                }
            }
    
            while (!stack.isEmpty()) { //栈非空
                Integer cur = stack.removeLast(); //取栈顶顶点 cur
                topo.add(cur); //将 cur 保存在拓扑序列数组 topo 中
                // 遍历 cur 的邻接点
                for (Integer next : graph.get(cur)) {
                    inDegree.put(next, inDegree.get(next) - 1); // cur 的每个邻接点的入度减 1
                    if (inDegree.get(next) == 0) //若入度减为 0, 则入栈
                        stack.push(next);
                }
                // 迭代器遍历
    //            Iterator<Integer> iter = graph.get(cur).iterator();
    //            while (iter.hasNext()) {
    //                Integer next = iter.next();
    //                inDegree.put(next, inDegree.get(next) - 1); // cur 的每个邻接点的入度减 1
    //                if (inDegree.get(next) == 0) //若入度减为 0, 则入栈
    //                    stack.push(next);
    //            }
    
            }
            // index < inDegree.size() 时, 图存在回路, 不存在拓扑排序
            return topo.size() >= inDegree.size();
        }
    
        public static void main(String[] args) {
            // 实现创建好 用于保存 入度 和 图 的集合。
            Map<Integer, Integer> inDegree = new HashMap<>();
            Map<Integer, List<Integer>> graph = new HashMap<>();
            // 边集合, 其中 ref 为起点, bean 为终点
            ArrayList<Reference> references = new ArrayList<>(){{
                add(new Reference(0, null));
                add(new Reference(1, 0));
                add(new Reference(1, 2));
                add(new Reference(2, 0));
                add(new Reference(3, 0));
                add(new Reference(3, 5));
                add(new Reference(4, 2));
                add(new Reference(4, 3));
                add(new Reference(4, 5));
                add(new Reference(5, null));
            }};
            // 根据边集合, 创建 图, 并计算 入度
            CreateGraphAndInDegree(references, graph, inDegree);
    
            // 实现创建好 用于 拓扑排序的 集合
            ArrayList topo = new ArrayList();
            TopologicalSort(graph, inDegree, topo);
    
            System.out.println(topo.toString());
        }
    }
    

    更多相关内容
  • 拓扑排序是将有向无环图的顶点排成一个线性序列的过程。 比如可将上 三、拓扑排序步骤 1. 首先要任意选择一个没有前驱的顶点,即入度为0的点,然后将它输出。 在下面这张图中我们选择1为出发点。 2. ...

    一、有向无环图

    • 有方向
    • 没有闭环

    有向图的拓扑排序

     

    二、拓扑排序

    拓扑排序是将有向无环图的顶点排成一个线性序列的过程。

    比如可将上图

     

    三、拓扑排序步骤

    1. 首先要任意选择一个没有前驱的顶点,即入度为0的点,然后将它输出。

    在下面这张图中我们选择1为出发点。

    有向图的拓扑排序

     

    2. 删除该节点以及与它相关联的所有边,这个点的nexts里面的点入度就减少1

    选择1为出发点之后,我们将它输出,并删除该节点以及与它相关联的所有边。

     

    有向图的拓扑排序

     

    3. 然后在删除后的图中继续找一个没有前驱的节点,

    这里没有前驱的节点只有2和3.

    这里我们选择3,那么将节点3输出后的图 如下图所示。

    有向图的拓扑排序

     

    4. 继续找入度为0的点重复上述步骤。

    接下来没有前驱的节点只有2和6了。

    我们这里选择节点6,同样的输出节点6后删除。

    然后继续找没有前驱的节点。这时候没有前驱的节点只剩下节点2.

     

    有向图的拓扑排序

    接下来的点继续进行拓扑排序,

     

    5. 得到的拓扑排序的一种如下图所示。

    有向图的拓扑排序

     

    相信大家也都发现其实拓扑排序是不唯一的,我们选择的出发点不同,结果就是不一样的。

    这里给出大家针对上图几种拓扑排序序列。

    有向图的拓扑排序

     

     

    四、算法设计

    用一个map保存他的所有节点对应的入度。

    用一个队列保存每次入度为0的节点然后pop,pop的顺序就是拓扑排序的顺序。

     

    list <node*> tsort(graph G) {
    	list<node*> res;
    	unordered_map<node*, int> inmap; 
    	queue<node*> zeronode;
    	for (auto next : G.nodes) {//G.nodes是map
    		inmap[next.second] = next.second->in;
    		if (next.second->in == 0) {
    			zeronode.push(next.second);
    		}
    	}
    	while (!zeronode.empty()) {
    		node* cur = zeronode.front();
    		zeronode.pop();
    		res.push_back(cur);
    		for (auto next : cur->nexts) {
    			inmap[next]--;
    			if (inmap[next] == 0) {
    				zeronode.push(next);
    			}
    		}
    	}
    
    	return res;
     }

     

     

    https://jingyan.baidu.com/article/1e5468f9c2c830094861b769.html

     

    https://zhuanlan.zhihu.com/p/127190494

    展开全文
  • 存储方式: 邻接表方式存储 class Node{ int visited = 0; // 是否被访问 int d = 0; // 初次遍历时间戳 int f = 0; // 最终遍历时间戳 LinkedList<Integer> linkNodes = null; public Node(){ ...

    图存储方式:
    邻接表方式存储

    class Node{
        int visited = 0; // 是否被访问
        int d = 0; // 初次遍历时间戳
        int f = 0; // 最终遍历时间戳
        LinkedList<Integer> linkNodes = null;
        public Node(){
            linkNodes = new LinkedList<>();
        }
    }
    

    以上代码为一个节点的数据结构,最终用一个数组储存所有节点
    求解: 深度优先算法
    在使用dfs进行遍历时节点要记录两个索引,即以上代码中的 d 和 f。d 的值表示该节点第一次被遍历到时的时间戳,f 的值表示再次回溯到该节点的时间戳。该时间戳 f 与 d 共享,每遍历一个节点加一。
    最终的拓扑排序以 f 的值降序排序。

    java代码:

    import java.util.ArrayList;
    import java.util.LinkedList;
    import java.util.Scanner;
    
    /**
     * 求有向无环图的拓扑结构
     */
    public class TopoLogical {
        static int time = 0; //记录遍历次序
        static ArrayList<Integer> topology; // 记录拓扑顺序的反序
        static Node[] nodes;
        public static void main(String[] args){
            /**
             * 输入方式:
             * 第一行输入节点的个数n
             * 后面n行输入第n个节点(从0开始数)链接的子节点,没有子节点则直接换行
             */
            Scanner input = new Scanner(System.in);
            int n = input.nextInt();
            nodes = new Node[n];
            topology = new ArrayList<>();
            for(int i = 0; i < n; ++i){
                nodes[i] = new Node();
            }
    
            input.nextLine();
    
            for(int i = 0; i < n; ++i){
                String line = input.nextLine();
                if(!line.equals("")){
                    String[] tempIntStr = line.split(" ");
                    for(int j = 0; j < tempIntStr.length; ++j){
                        nodes[i].linkNodes.add(Integer.parseInt(tempIntStr[j]));
                    }
                }
            }
            for(int i=0;i<nodes.length;i++){
                dfs(nodes[i], i);
            }
            System.out.print("拓扑顺序: ");
            for(int i=topology.size()-1;i>=0;i--){
                System.out.print(topology.get(i) + " ");
            }
        }
        static void dfs(Node node,int index){
            if(node.visited==1 || node==null)
                return;
            time ++;
            node.d = time;
            node.visited = 1;
            for(int i : node.linkNodes){
                Node current = nodes[i];
                if(current.visited == 0)
                    dfs(current, i);
            }
            topology.add(index);
            time ++;
            node.f = time;
        }
    
    }
    
    class Node{
        int visited = 0; // 是否被访问
        int d = 0; // 遍历次序
        int f = 0; // 最终遍历次序
        LinkedList<Integer> linkNodes = null;
        public Node(){
            linkNodes = new LinkedList<>();
        }
    }
    
    展开全文
  • 算法7-12:有向无环图拓扑排序 时间限制: 1 Sec 内存限制: 32 MB 题目描述 由某个集合上的一个偏序得到该集合上的一个全序,这个操作被称为拓扑排序。偏序和全序的定义分别如下: 若集合X上的关系R是自反的、反对...

    问题:有向无环图的拓扑排序

    题目描述
    由某个集合上的一个偏序得到该集合上的一个全序,这个操作被称为拓扑排序。偏序和全序的定义分别如下:
    若集合X上的关系R是自反的、反对称的和传递的,则称R是集合X上的偏序关系。
    设R是集合X上的偏序,如果对每个x,y∈X必有xRy或yRx,则称R是集合X上的全序关系。
    由偏序定义得到拓扑有序的操作便是拓扑排序。
    拓扑排序的流程如下:

    1.   在有向图中选一个没有前驱的顶点并且输出之;
      
    2.   从图中删除该顶点和所有以它为尾的弧。
      

    重复上述两步,直至全部顶点均已输出,或者当前图中不存在无前驱的顶点为止。后一种情况则说明有向图中存在环。
    采用邻接表存储有向图,并通过栈来暂存所有入度为零的顶点,可以描述拓扑排序的算法如下:

    在本题中,读入一个有向图的邻接矩阵(即数组表示),建立有向图并按照以上描述中的算法判断此图是否有回路,如果没有回路则输出拓扑有序的顶点序列。

    输入
    输入的第一行包含一个正整数n,表示图中共有n个顶点。其中n不超过50。
    以后的n行中每行有n个用空格隔开的整数0或1,对于第i行的第j个整数,如果为1,则表示第i个顶点有指向第j个顶点的有向边,0表示没有i指向j的有向边。当i和j相等的时候,保证对应的整数为0。
    输出
    如果读入的有向图含有回路,请输出“ERROR”,不包括引号。
    如果读入的有向图不含有回路,请按照题目描述中的算法依次输出图的拓扑有序序列,每个整数后输出一个空格。
    请注意行尾输出换行。
    样例输入
    4
    0 1 0 0
    0 0 1 0
    0 0 0 0
    0 0 1 0
    样例输出
    3 0 1 2

    n=0
    G=[[] for i in range(100)]
    inDegree=[0]*100
    def topologicalSort():
        global n,G,inDegree
        list=[]
        list_1=[]
        num=0
        for i in range(0,n):
            if inDegree[i]==0:
                list.append(i)
        while len(list)!=0:
            t=list[-1]
            list_1.append(t)
            list.pop(-1)
            for i in range(len(G[t])):
                v=G[t][i]
                inDegree[v]-=1
                if inDegree[v]==0:
                    list.append(v)
            G[t].clear()
            num+=1
        if num==n:
            for i in list_1:
                print(i,end=' ')
        else:
            print("ERROR")
    if __name__=='__main__':
        n = int(input())
        for i in range(0, n):
            ar = input()
            b = ar.split(' ')
            graph = [int(i) for i in b]
            for j in range(len(graph)):
                if graph[j] == 1 and i!=j:
                    inDegree[j]+=1
                    G[i].append(j)
        topologicalSort()
        print()
    
    展开全文
  • 带你了解有向无环图拓扑排序

    千次阅读 2020-04-09 19:19:42
    在图论中,如果一个有向图无法从某个顶点出发经过若干条边回到该点,则这个图是一个无环图(DAG图)。而提到DAG,就差不多会联想到拓扑排序拓扑排序是指由某个集合上的一个偏序得到该集合上的一个全序的操作。...
  • 有向无环图拓扑排序 有向无环图 (DAG) 的拓扑排序是顶点的线性排序,使得对于每个有向边 u, v,顶点 u排在 v 之前。如果不是 DAG,则无法对图进行拓扑排序。 例如,下拓扑排序是「5 4 2 3 1 0」。一个...
  • 有向无环图拓扑排序算法
  • final为最终代码,其他为测试代码,有向无环图进行拓扑排序若不是DAG则输出圈
  • 求出有向无环图的所有拓扑排序序列的C语言程序实现
  • 无环图是指一个无环有向图无环图可用来描述工程或系统的进行过程,如一个工程的施工图、学生课程间的制约关系图等。用顶点表示活动,用弧表示活动间优先关系的无环图,称为顶点表示活动的网,简称...
  • 在一个程序中,两种执行关系,一种是a执行完了才能执行b,也就是串行;另外一种是a和b没有依赖关系,a和b可以同时执行,也就是并行。 如上图所示,这是一个无环图,箭头表示事件之间的...(对于有向图:节点(..
  • 拓扑排序只适用于有向无环图 拓扑排序规则如下: (1) 从有向无环图中选择一个没有前驱(入度为0)的顶点vertex,并将顶点输出 (2)从中删除这个顶点,同时删除从该顶点出发的所有边 本文算法复杂度为 O(n+e); ...
  • 算法 有向无环图 拓扑排序

    千次阅读 2017-05-23 11:48:21
    1. 如何构造图 邻接矩阵(二维数组) 图的邻接矩阵存储方式是用两个数组来表示图,一个一维数组存储图中顶点信息,一个二维数组存储图中边或弧的...无向图成为顶点v的边表,有向图成为顶点v作为弧尾的出边表。等等其他表
  • NULL 博文链接:https://128kj.iteye.com/blog/1675685
  • 有向无环图的所有拓扑排序有向无环图DAG的拓扑排序是顶点的线性排序,从而使每一有向边[u,v][u,v][u,v],顶点u进来的顺序v在。如果不是 DAG,则无法对图进行拓扑排序。 给定一个 DAG,打印的所有拓扑排序。 ...
  • 几个基本概念 1.有向图:顾名思义,从一个点到另一个点,并非向无向图那样默认双向可达(只要两点之间存在边就相互可达),有向图是严格按照方向单向可达。...3.无环图:一个不存在有向图。 ...
  • 本篇文章给大家带来的内容是关于Python实现有向无环图拓扑排序代码示例,一定的参考价值,需要的朋友可以参考一下,希望对你有所帮助。Python有向无环图拓扑排序拓扑排序的官方定义为:由某个集合上的一个...
  • 拓扑排序原理:对有向无环图DAG的顶点进行排序,使得对于每一条有向边(u,v),均u(在排序记录中)比v先出现,亦可理解为对某点v而言,只有当v的所有源点均出现了v才能出现 根据定义,可以得到以下两个结论: 如果...
  • 对于无环图中的任意节点u、v,若其中一个是另一个父节点或者祖先结点,则dfs时孩子结点总是会先结束,即祖先结点结束的时间必然大于孩子结点的时间;若两者没有关系,则在拓扑排序中的顺序也没关系。所以设置一个...
  • //返回有向图G中顶点v的第一个邻接点,如没有返回-1 int FirstNeighbor(Graph G, int v) { //(......具体实现细节) return w; } //假设图G中w是v的一个邻接点,返回除w外的v的下一个邻接点,若没有,返回-1 int ...
  • 拓扑排序应用于有向无环图之中,排序完以后会出现这样的性质:对于一个点p,只对排序位置在它之后的点边。如果有环,则上的点以及上点所能到达的点都不会被放进拓扑序列中。 举例来说,如果我们将一系列需要...
  • Kahn算法 拓扑排序
  • 有向无环图(DAG)才有拓扑排序,非DAG没有拓扑排序一说。一般用有向边指示顺序关系,运用于顺序关系。例如,下面这个:显然是一个DAG,1→4表示4的入度+1,4是1的邻接点,代码表示:前者deg[4]++;后者用vector[1...
  • 无环拓扑排序与关键路径 一:基本概念 1:无环图 一个无环有向图称做无环图(Directed Acyclic Graph)。简称DAG 图。DAG 图是一类较有向树更一般的特殊有向图, 2:拓扑排序 对一个...
  • 问题 给定一个有向无环图和一个源点,求从该源点到其他所有的顶点的最短路径。我们已经知道了如何...关于有向无环图(DAG)我们首先要知道,它们可以很容易地进行拓扑排序拓扑排序应用范围非常之广,其中最常用的
  • 对于有向无环图的摘抄

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 24,918
精华内容 9,967
关键字:

有向无环图的拓扑排序

友情链接: The-search-string.zip