拓扑排序
dag 的拓扑排序
DAG的拓扑排序DAG(Directed Acyclic Graph)是一种有向无环图,它是由若干个节点和它们之间的有向边组成的。
DAG在计算机科学中有着广泛的应用,如任务调度、编译器、数据流分析等。
在DAG中,节点之间的边只能从前往后指向,不能形成环路,这是DAG与普通的有向图的最大区别。
拓扑排序是DAG中一种常见的排序算法,本文将详细介绍DAG的拓扑排序。
一、DAG的拓扑排序定义DAG的拓扑排序是指将DAG中的所有节点排成一条线性序列,使得对于任何一条有向边(u,v),节点u都排在节点v的前面。
如果DAG中存在环路,则无法进行拓扑排序。
二、DAG的拓扑排序算法DAG的拓扑排序算法主要有两种:Kahn算法和DFS算法。
1. Kahn算法Kahn算法是一种基于贪心的算法,它的基本思想是从DAG中选择一个没有前驱节点的节点,将其输出并从DAG中删除,然后更新剩余节点的入度。
不断重复这个过程,直到所有节点都被输出。
如果DAG中存在环路,则无法进行拓扑排序。
下面是Kahn算法的伪代码:1. 初始化一个队列Q,将所有入度为0的节点加入队列中2. 取出队首节点u,并输出3. 对u的所有后继节点v进行操作:a. 将v的入度减1b. 如果v的入度为0,则将v加入队列Q中4. 重复步骤2-3,直到队列为空如果在执行过程中,某个节点的入度始终不为0,则说明存在环路,无法进行拓扑排序。
2. DFS算法DFS算法是一种基于深度优先搜索的算法,它的基本思想是对DAG进行深度优先搜索,每次搜索到一个节点时,将其标记为已访问,并递归地访问其所有后继节点。
当某个节点的所有后继节点都被访问过时,将其加入结果序列中。
最终得到的结果序列就是DAG的拓扑排序结果。
如果在搜索过程中遇到了已经访问过的节点,则说明存在环路,无法进行拓扑排序。
下面是DFS算法的伪代码:1. 初始化一个栈S,将所有未访问的节点加入栈中2. 取出栈顶节点u,并标记为已访问3. 对u的所有后继节点v进行操作:a. 如果v未被访问,则递归访问v4. 将u加入结果序列中5. 重复步骤2-4,直到栈为空最终得到的结果序列就是DAG的拓扑排序结果。
24年408算法题拓扑排序
24年408算法题拓扑排序给定一个包含24个节点和408条边的有向图,并且需要进行拓扑排序,可以按照以下步骤进行:
1. 创建一个空列表或队列,用于存储拓扑排序的结果。
2. 遍历所有的节点,并找到入度为0的节点(即没有任何依赖关系的节点)。
3. 将入度为0的节点加入到拓扑排序结果中,并将其从图中移除,同时更新相关节点的入度。
4. 重复步骤2和步骤3,直到所有节点都被加入到拓扑排序结果中或无法找到入度为0的节点。
5. 如果存在未被加入到拓扑排序结果中的节点,说明图中存在环路,无法进行拓扑排序。
图论-拓扑排序与关键路径
图论算法四、拓扑排序算法与关键路径,都是在有向无今天讨论的所有算法,今天讨论的所有算法环图中才适用的。
1.拓扑排序AOV(Activity On Vertex Network)顶点活动网络。
所谓AOV网,就是指一个有向无环图。
我们可以把一个AOV网形象地看成是一个大任务的进行流程。
其中的子任务有一定的先后关系,必须完成一个,或某几个子任务后,才能接着完成接下来的任务。
因此,找出子任务的完成顺序就显得很有意义。
显然把每一个任务的前驱任务都放在这个任务之前,就是所有任务的完成顺序。
这样的顺序就叫做“拓扑序列”。
注意,拓扑序列是不唯一的。
构造拓扑序列的拓扑排序算法思想很简单:1)选择一个入度为0的顶点并输出2)然后从AOV网中删除此顶点及以此顶点为起点的所有关联边;3)重复上述两步,直到不存在入度为0的顶点为止。
4)若输出的顶点数小于AOV网中的顶点数,则输出“有回路信息”,否则输出的顶点序列就是一种拓扑序列。
从第四步可以看出,拓扑排序可以用来判断一个有向图是否有环。
只有有向无环图才存在拓扑序列。
算法实现:a)数据结构:indgr[i]:顶点i的入度;stack[]:栈b)初始化:top=0(栈顶指针)c)将初始状态所有入度为0的顶点压栈d)I=0(计数器)e)While栈非空(top>0)doi.顶点v出栈;输出v;计数器增1;ii.For与v邻接的顶点u do1.dec(indgr[u]);2.If indgr[u]=0then顶点u入栈f)EXIT(I=|V|)简单&高效&实用的算法。
上述实现方法复杂度O(V+E)关键路径2.2.关键路径如果我们已经得到了完成任务的顺序,即拓扑序列,接下来我们最关心的问题显然是完成所有的任务最少需要多长时间。
假设工程可以同时进行。
我们开始时可以人为添加2个结点,0和n+1,表示任务开始和任务全部完成。
因为所有的任务都必须完成,根据木桶原理,耗时最长的那条路显然应是完成所有任务的最少耗时。
什么是拓扑排序?
什么是拓扑排序?
拓扑排序是一种对有向无环图(DAG)进行排序的算法,它可以将图中的顶点
按照一定的顺序排列,使得图中任意一条边的起点在排列中都出现在终点之前。
具体来说,拓扑排序的过程是这样的:
1. 首先,找到图中入度为0的顶点(即没有任何边指向它的顶点),将其加入到排序的结果中。
2. 然后,移除这个顶点以及由它出发的所有边,更新剩余顶点的入度。
3. 重复以上步骤,直到所有的顶点都被加入到排序结果中或者发现图中存在环。
如果最终所有的顶点都被加入到排序结果中,那么这个排序就是图的一个拓扑
排序;如果在过程中发现了环,那么图不具有拓扑排序。
拓扑排序的应用非常广泛,比如在软件工程中可以用来解决模块的依赖关系,
或者在任务调度中确定任务的执行顺序等等。
这个算法的时间复杂度为O(V+E),其中V为顶点的数量,E为边的数量。
图基本算法拓扑排序(基于dfs)
图基本算法拓扑排序(基于dfs) 拓扑排序,是对有向⽆回路图进⾏排序,以期找到⼀个线性序列,这个线性序列在⽣活正可以表⽰某些事情完成的相应顺序。
如果说所求的图有回路的话,则不可能找到这个序列。
在⼤学数据结构课上,我们知道求拓扑排序的⼀种⽅法。
⾸先⽤⼀个⼊度数组保存每个顶点的⼊度。
在进⾏拓扑排序时,我们需要找到⼊度为0的点,将其存⼊线性序列中,再将其从图中删除(与它相关的边都删除,相邻的顶点的⼊度均减1),再重复上⾯的操作,直⾄所有的顶点都被找到为⽌。
如果不对每次找⼊度为0的顶点的⽅法进⾏处理,⽽直接去遍历⼊度数组,则该算法的时间复杂度为O(|V|2),如果使⽤⼀个队列来保存⼊度为0的顶点,则可以将这个算法的复杂度降为O(V+E)。
今天在算法导论上看了⽤dfs来求拓扑排序的算法,才发现其⾼深之处,膜拜之Orz…下⾯是算法导论的叙述: 本节说明了如何运⽤深度优先搜索,对⼀个有向⽆回路图(dag)进⾏拓扑排序。
对有向⽆回路图G=(V,E)进⾏拓扑排序后,结果为该图顶点的⼀个线性序列,满⾜如果G包含边(u, v),则在该序列中,u就出现在v的前⾯(如果图是有回路的,就不可能存在这样的线性序列)。
⼀个图的拓扑排序可以看成是图中所有顶点沿⽔平线排列⽽成的⼀个序列。
使得所有的有向边均从左指向右。
因此,拓扑排序不同于通常意义上的排序。
在很多应⽤中,有向⽆回路图⽤于说明时间发⽣的先后次序,下图1即给出⼀个实例,说明Bumstead教授早晨穿⾐的过程。
他必须先穿好某些⾐服,才能再穿其他⾐服(如先穿袜⼦后穿鞋),其他⼀些⾐服则可以按任意次序穿戴(如袜⼦和裤⼦),在图1中,有向边<u,v>表⽰⾐服u必须先于⾐服v穿戴。
因此,该图的拓扑排序给出了⼀个穿⾐的顺序。
图2说明了对该图进⾏拓扑排序后,将沿⽔平线⽅向形成⼀个顶点序列,使得图中所有有向边均从左指向右。
拓扑排序算法具体步骤如下:1、调⽤dfs_travel();2、在dfs_travel()每次调⽤dfs()的过程中,都记录了顶点s的完成时间,将顶点s按完成顺序保存在存放拓扑排序顺序的数组topoSort[]中。
DAG及拓扑排序
DAG及拓扑排序1.有向⽆环图和拓扑排序有向⽆环图(Directed Acyclic Graph,简称DAG);拓扑排序指的对DAG⼀个有序的线性排列。
即每次选出⼀个没有⼊度的节点,然后输出该点并将节点和其相关连的弧都删除(此时均为以该节点为弧头的弧),依次进⾏,直⾄遍历所有节点,就是⼀个DAG的拓扑排序,值得⼀提的是⼀个图的拓扑排序不⼀定是唯⼀的,很有可能有若⼲个排序。
不过这样仍然不太清楚,我们以图来展⽰。
上述过程即为⼀个拓扑排序,⾸先对于该DAG来说,只有A和E是⽆⼊度的节点,任选⼀个E删除,接着删除相应的弧。
【输出E】同样此时只有A变成⽆⼊度节点,做同样的操作。
【输出A】删除A后只有顶点C和G没有前驱,仍然任选⼀个删除,依此类推,可以得到⼀个该图的拓扑排序。
EAGCFB2.拓扑排序的实现前⾯深搜⼴搜已经⽤邻接矩阵实现⽆向图了,这⾥我们使⽤邻接表来表⽰有向图。
先来复习⼀下邻接表对于这样的数据结构应该怎么实现呢?如果你第⼀眼看上去觉得这就是若⼲个链表组成的,那么恭喜你回答正确,我们⼀般都是使⽤链表的思想来实现邻接表的。
因此我们⾸先要在域中定义⼀个链表的数组:private Ljtable [] vertex;然后定义链表和节点类class Ljtable {char data;Node head;public Ljtable(char c,int n){data = c;head = new Node(n);}}{number = a;next = null;}}拓扑排序,纯本⼈⼿写,因为我的代码会使各节点的⼊度发⽣变化,因此需要提前存储,拓扑排序后在复原,看起来有点蠢。
不过由于都是顺序排列,所以时间复杂度还好。
public void Topo(){int [] m = new int [vertex.length];for (int i = 0; i < vertex.length; i++){m[i] = vertex[i].inDegree;}int k = 0;while(k < vertex.length)for (Ljtable l:vertex){if(l.inDegree == 0) {System.out.print(l.data);k++;Node h = l.head;while(h!=null) {vertex[h.number].inDegree--;h = h.next;}}}for (int i = 0; i < vertex.length; i++){vertex[i].inDegree = m[i];}}完整代码请看。
拓扑算法应用
拓扑算法应用
拓扑排序是对一个有向无环图(DAG)的所有顶点进行线性排序,它必须满足两个条件:每个顶点出现且只出现一次,若存在一条从顶点A到顶点B的路径,那么在序列中顶点A出现在顶点B的前面。
拓扑排序常常用于确定事物发生的顺序。
这种算法在多个领域有实际应用:
1.项目管理:在工程项目或系统过程中,可以将每个子工程视为
一个顶点,如果一个子工程的开始必须在另一个子工程完成之
后,那么就在这两个子工程之间画一条有向边。
通过拓扑排序,可以确定所有子工程的执行顺序,从而确保工程能顺利进行,
并计算出整个工程完成所需的最短时间。
2.编译器优化:在编译器设计中,拓扑排序也被广泛应用。
例如,
在VS中创建一个MVC的解决方案XMedia,如果项目A引用项
目B,则表示A依赖B,所以必须先编译项目B,再编译项目A。
通过拓扑排序,可以确定项目的编译顺序,从而优化编译过程,提高编译效率。
3.表达式优化:在描述含公共子式的表达式的工具中,拓扑排序
也可以用于实现对相同子式的共享,从而节省存储空间。
总的来说,拓扑排序算法在项目管理、编译器优化、表达式优化等多个领域都有重要的应用。
拓扑排序求最短路径
拓扑排序和最短路径是两个不同的图论问题,它们在解决实际应用问题时有着重要的应用。
首先,让我们解释一下拓扑排序,然后再来看如何用拓扑排序来解决最短路径问题。
拓扑排序:拓扑排序是用于有向无环图(Directed Acyclic Graph, DAG)的一种排序方式,通常用于对事件或活动的排序。
这种排序方法假设事件之间的依赖关系通过有向边表示,并且没有环路。
拓扑排序的结果是一个线性序列,其中每个节点都出现在其直接依赖节点之后。
拓扑排序在项目管理、调度和决策制定等领域有广泛应用。
最短路径问题:最短路径问题是图论中的一个经典问题,它要求找到图中两个节点之间的最短路径。
通常使用Dijkstra算法或Floyd-Warshall算法来解决这个问题。
最短路径问题在路径规划、网络路由和物流优化等领域有广泛应用。
如何用拓扑排序求最短路径:1. **确定拓扑排序**:首先,使用拓扑排序算法(如Kahn算法)对有向无环图进行排序。
这个排序将给出图中所有节点的线性顺序。
2. **计算最短路径**:基于拓扑排序的顺序,可以很容易地找到两个节点之间的最短路径。
如果节点i和节点j在拓扑排序中的位置分别为i和j,那么从i到j的最短路径长度就是图中从i到j的边的权重中较小的那个(如果存在的话)。
如果没有这样的边,那么两个节点之间没有直接路径,最短路径为无穷大。
这个方法的关键在于利用拓扑排序的线性特性,通过简单观察就可以找到最短路径。
**注意**:这个方法只适用于无环图(DAG)。
如果图中存在环路,拓扑排序就无法使用,需要使用其他方法(如Tarjan算法)来处理。
下面是一个简单的Python代码示例,展示了如何使用拓扑排序来找到两个节点之间的最短路径:```pythonfrom collections import defaultdict, dequedef topological_sort(graph):# 计算每个节点的入度并找出入度为0的节点作为起点in_degree = {node: 0 for node in graph}for node in graph:for neighbor in graph[node]:in_degree[neighbor] += 1return_nodes = [node for node in graph if in_degree[node] == 0]return return_nodes, [graph[node] for node in return_nodes]def shortest_path(graph, start, end):# 使用拓扑排序的结果来查找最短路径# 假设每个节点的入度为0的节点在结果列表中位于列表的开头queue = deque([start]) # 使用队列进行广度优先搜索visited = set() # 记录已访问的节点while queue:node = queue.popleft()if node == end: # 找到目标节点,返回路径长度和路径上的节点path_length = 0current = nodewhile current in visited: # 确保路径上的节点按顺序访问current = graph[current][0] # 获取下一个邻居节点path_length += 1visited.add(node) # 将已访问的节点添加到已访问集合中return path_length, current + [end] # 返回路径长度和完整路径(不包括起始节点)for neighbor in graph[node]: # 继续探索当前节点的邻居节点(下一个步长)if neighbor not in visited: # 如果邻居节点尚未访问过queue.append(neighbor) # 将邻居节点加入队列中以供下一步搜索return None, None # 如果找不到目标节点,返回None```以上代码使用了一个字典来表示图的结构,其中键是节点名称,值是一个包含相邻节点的列表。
拓扑排序结果字典序
拓扑排序结果字典序全文共四篇示例,供读者参考第一篇示例:拓扑排序是一种对有向无环图(DAG)进行排序的方法,它可以保证图中的每个顶点在排序结果中出现的位置都是正确的。
拓扑排序的结果并不唯一,即可能存在不同的排列顺序,但这些顺序都满足拓扑排序的要求。
在拓扑排序中,我们通常会使用一种算法来进行排序,这种算法被称为拓扑排序算法。
其中最经典的算法是Kahn算法,其基本思想是通过不断地删除入度为0的顶点,直到所有顶点都被删除。
删除的顶点即为拓扑排序的结果。
在Kahn算法中,我们会使用一个队列来存储入度为0的顶点,并在每次删除一个顶点后更新其他顶点的入度。
拓扑排序的结果可以有多种形式显示,最常见的形式是将顶点按照其排序顺序输出。
但在某些情况下,我们可能会需要将拓扑排序结果按照字典序进行输出,这时我们需要对拓扑排序的结果进行一定的调整。
在进行拓扑排序时,我们可以将排序结果存储在一个列表中,并通过比较列表中的元素大小,从而得到字典序的拓扑排序结果。
这种方法相对简单直接,但需要注意的是,不同的图可能有不同的结果,因此需要在编程时考虑到这一点。
对于拓扑排序结果的字典序,我们可以举一个例子来说明。
假设我们有一个图G,其拓扑排序结果为{A, B, C, D}。
如果我们需要按照字典序输出拓扑排序结果,那么最终的结果可能会是{A, B, D, C}或者{B, A, D, C}等不同的排列。
除了使用队列和比较列表元素大小之外,我们也可以通过递归的方式来实现拓扑排序结果的字典序输出。
在递归过程中,我们可以利用字典序的性质,对拓扑排序结果进行调整,从而输出符合字典序要求的结果。
拓扑排序结果的字典序输出是一个比较有意义的问题,在实际工程中也有一定的应用场景。
通过合理的算法设计和编程实现,我们可以方便地得到符合要求的拓扑排序结果,从而更好地解决问题。
希望本文能够帮助读者更好地理解拓扑排序以及相关概念,进一步拓展对图论的认识。
第二篇示例:拓扑排序是一种用于有向图的排序方法,它可以将图中的节点按照一定的顺序排列。
数据结构拓扑排序实验报告
数据结构拓扑排序实验报告正文:一、实验目的本实验旨在通过实现拓扑排序算法来加深对数据结构中图的相关概念的理解,掌握拓扑排序的具体步骤与实现方法。
二、实验原理拓扑排序是一种对有向无环图进行排序的算法,它可以将有向无环图的顶点按照线性的顺序排列出来,使得对于任何一个有向边(u, v),都有顶点 u 在排列中出现在顶点 v 之前。
拓扑排序常用于表示图中的依赖关系,如任务调度、编译顺序等场景。
三、实验步骤1. 构建有向图根据实际需求构建有向图,可以使用邻接表或邻接矩阵等数据结构来表示有向图。
2. 执行拓扑排序算法利用拓扑排序算法对构建的有向图进行排序,可选择使用深度优先搜索(DFS)或广度优先搜索(BFS)等算法实现。
3. 输出排序结果将排序后的顶点按照线性的顺序输出,得到拓扑排序的结果。
四、实验结果与分析1. 实验数据以图 G = (V, E) 的顶点集合 V 和边集合 E,构建了如下的有向图:V = {A, B, C, D, E, F}E = {(A, C), (B, C), (C, D), (D, E), (E, F)}2. 拓扑排序结果经过拓扑排序算法的处理,得到的拓扑排序结果如下: A, B, C, D, E, F3. 结果分析可以看出,根据有向图的依赖关系,拓扑排序算法能够将顶点按照合理的顺序进行排序。
拓扑排序的结果可以作为图中顶点的执行顺序,具有重要的应用价值。
五、实验总结通过本次实验,我们深入学习了拓扑排序算法,并成功实现了拓扑排序的过程。
拓扑排序在图论和数据结构中具有广泛的应用,对于理解和解决与图相关的问题具有重要意义。
六、附件本文档没有涉及附件内容。
七、法律名词及注释本文档没有涉及法律名词及注释。
拓扑排序讲解+例题
拓扑排序讲解+例题对⼀个==有向⽆环图(Directed Acyclic Graph简称DAG)==G进⾏拓扑排序,是将G中所有顶点排成⼀个线性序列,使得图中任意⼀对顶点u和v,若边<u,v>∈E(G),则u在线性序列中出现在v之前。
通常,这样的线性序列称为满⾜拓扑次序(Topological Order)的序列,简称拓扑序列。
简单的说,由某个集合上的⼀个偏序得到该集合上的⼀个全序,这个操作称之为拓扑排序。
⽐如说给定若⼲个两个元素之间的⼤⼩关系,要转换成所有元素的总体⼤⼩关系,就可以⽤拓扑排序来处理下⾯给出的例题就是这个样⼦关于拓扑排序还有⼀种⽤法->判断给定的有向图中是否存在环下⾯来说明⼀下拓扑排序的相关步骤:(默认已经将图存好)⾸先统计所有点的⼊度,然后将所有点⼊度为0的所有点放进队列(根据题⽬特殊要求也可以使⽤优先队列)然后采取像BFS那样的⽅式,当队列⾮空的时候,始终取队列头端的⼀个元素,并将这个元素记录下来之后pop掉,把这个点(⽐如说是点P)放进⽤来存储拓扑序列的不定长数组vector中,然后遍历和这个点相连的所有点,并将与点P相连的所有点的⼊度减少1(换句话说讲点P去掉之后,他的指向关系就会消失,并且以他为起点的所有点的⼊度都会减⼩1),如果这个点的⼊度刚好为0,那么正好可以将这个点继续放到队列中,如此反复伪代码如下void top_sort(){for(int i=1;i<=n;i++){if(⼊读为0) que.push(i);}while(que.size()){int top = que.top()/que.front();que.pop();vet.push_back(这个点->top);for(int i=1;i<=n;i++){///在数据范围⾜够⼩的情况下可以直接采取⼆维数组存图/// gra[i][j] == 1表⽰有⼀条从i指向j的边,反之没有if(相连){deg[i]--;if(deg[i] == 0) que.push(i);}}}}下⾯是⽐较常⽤的⼩模板:priority_queue <int, vector<int>, greater<int> >litt;int dege[507];int n,m;int gra[507][507];vector<int>vet;void top_sort(){for(int i=1;i<=n;i++){if(dege[i] == 0) litt.push(i);}while(litt.size()){int tp = litt.top();litt.pop();vet.push_back(tp);for(int i=1;i<=n;i++){if(gra[tp][i]){dege[i] --;if(dege[i] == 0) litt.push(i);}}}}下⾯来看例题:题⽬描述有N个⽐赛队(1<=N<=500),编号依次为1,2,3,。
拓扑排序求最长路径
拓扑排序求最长路径1. 什么是拓扑排序?拓扑排序是对有向无环图(DAG)进行排序的一种算法。
在有向图中,如果存在一条从节点A到节点B的有向边,那么节点A就必须在节点B之前进行排序。
拓扑排序通过将图中的节点按照一定的顺序进行排列,使得任意两个节点之间不存在环。
拓扑排序可以应用于许多问题,比如任务调度、依赖关系分析等。
2. 拓扑排序算法2.1. 算法原理拓扑排序算法基于深度优先搜索(DFS)或广度优先搜索(BFS)实现。
其基本思想是通过遍历图中的所有节点,并记录每个节点的入度(即指向该节点的边数)。
然后从入度为0的节点开始遍历,并将其加入结果列表中。
然后将与该节点相邻的节点的入度减1,并将新入度为0的节点加入结果列表。
重复此过程直到所有节点都被加入结果列表。
2.2. 算法步骤以下是拓扑排序算法的详细步骤:1.初始化一个空结果列表result和一个空队列queue。
2.遍历图中所有节点,并统计每个节点的入度。
3.将入度为0的节点加入队列queue。
4.当队列queue不为空时,执行以下步骤:–从队列queue中取出一个节点node,并将其加入结果列表result。
–遍历与节点node相邻的所有节点,并将它们的入度减1。
–如果某个节点的入度减为0,则将其加入队列queue。
5.如果结果列表result的长度等于图中的节点数,则说明拓扑排序成功;否则,说明图中存在环。
以下是使用Python语言实现拓扑排序算法的示例代码:from collections import defaultdict, dequedef topological_sort(graph):# 统计每个节点的入度in_degree = defaultdict(int)for node in graph:for neighbor in graph[node]:in_degree[neighbor] += 1# 初始化结果列表和队列result = []queue = deque()# 将入度为0的节点加入队列for node in graph:if in_degree[node] == 0:queue.append(node)# 拓扑排序while queue:node = queue.popleft()result.append(node)for neighbor in graph[node]:in_degree[neighbor] -= 1if in_degree[neighbor] == 0:queue.append(neighbor)if len(result) == len(graph):return resultelse:return None3. 拓扑排序求最长路径在有向无环图中,求最长路径可以通过拓扑排序算法来实现。
拓扑排序实验报告
拓扑排序实验报告第一点:实验背景及目的在计算机科学中,拓扑排序是一种针对有向无环图(DAG)的算法,其目的是对图中的所有节点进行排序,使得对于图中的每一条有向边(u,v),节点u在排序中都出现在节点v之前。
拓扑排序的应用非常广泛,如任务调度、编译单元的生成等。
本实验的目的在于使学生理解和掌握拓扑排序的基本概念和方法,培养学生运用拓扑排序解决实际问题的能力。
通过实验,学生应能熟练使用拓扑排序算法对给定的有向无环图进行排序,并理解算法的时间复杂度。
第二点:实验原理与方法拓扑排序算法的基本原理是先找到所有入度为0的节点,将它们加入结果序列中,然后从图中删除这些节点以及它们的出边,之后再找到新的入度为0的节点,重复上述过程,直到所有节点都被加入结果序列中或者图中仍有节点存在。
如果图中仍有节点存在,则说明图中含有环,无法进行拓扑排序。
实验中,我们将使用深度优先搜索(DFS)算法来实现拓扑排序。
具体方法是,从图中的任意节点开始,进行深度优先搜索,每当访问一个节点时,就将它从图中删除,并将其入边所指向的节点入度减一。
当某个节点的入度变为0时,将其加入结果序列中。
重复这个过程,直到所有节点都被删除或者图中仍有节点存在。
以上就是拓扑排序实验报告的第一点和第二点内容,希望能对你有所帮助。
第三点:实验环境与工具为了完成拓扑排序的实验,我们需要准备以下环境和工具:1.编程语言:本实验可以使用C++、Java、Python等编程语言完成。
在这里,我们推荐使用Python,因为Python具有简洁的语法和强大的第三方库支持,非常适合进行算法实验。
2.开发环境:Python开发者可以使用PyCharm、VSCode等集成开发环境进行实验。
这些开发环境提供了代码高亮、调试、语法检查等功能,可以提高开发效率。
3.第三方库:在Python中,我们可能需要使用如matplotlib、networkx等第三方库来绘制图和进行图的算法分析。
有向无环图的拓扑排序实验总结
有向无环图的拓扑排序实验总结
有向无环图(Directed Acyclic Graph, DAG)是有向图的一种,字面意思的理解就是图中没有环。
常常被用来表示事件之间的驱动依赖关系,管理任务之间的调度。
拓扑排序是对DAG的顶点进行排序,使得对每一条有向边(u, v),均有u(在排序记录中)比v 先出现。
亦可理解为对某点v而言,只有当v的所有源点均出现了,v才能出现。
拓扑排序是指由某个集合上的一个偏序得到该集合上的一个全序的操作。
拓扑排序常用来确定一个依赖关系集中,事物发生的顺序。
拓扑排序是对有向无环图的顶点的一种排序,它使得如果存在一条从顶点A到顶点B的路径,那么在排序中B出现在A的后面。
DAG在区块链中得到很广泛的应用哦。
图(Graph)是由顶点的有穷非空集合和顶点之间边的集合组成,通常表示为:G(V, E),其中,G表示一个图,V是图G中顶点的集合,E是图G中边的集合。
图按照边的有无方向性分为无向图和有向图。
图中某个节点与其他节点的直连边条数称为该节点的度。
有向图中,指向其他节点的边成为出度,被其他节点指向的边称为入度。
如果在有向图中,无法从某个顶点出发经过若干条边回到该点,则这个图是一个有向无环图(DAG图)。
偏序,集合内只有部分元素之间在这个关系下是可以比较的,比如:比如复数集中并不是所有的数都可以比较大小,那么“大
小”就是复数集的一个偏序关系。
全序,集合内任何一对元素在在这个关系下都是相互可比较的,比如:有限长度的序列按字典序是全序的。
最常见的是单词在字典中是全序的。
图的拓扑排序
图的拓扑排序图的拓扑排序(TopologicalSorting)是指将一个有向无回路的图的顶点排序的方法。
它的表示和排序经常是一个有向图的核心。
有许多应用,用拓扑排序实现,比如活动安排问题、调度问题等,也能应用在计算机科学中,比如求解依赖关系等。
图的拓扑排序实际上是个结构优化问题,要求以最少的顶点构成图,使得它能够完整描述一个有向无回路图。
“拓扑排序”这个名称源自拓扑学,它是一种研究许多结构的学科,主要是用来描述无自循环的有向图的结构特性。
拓扑排序则是根据拓扑学来对给定的图进行排序,以使其有序化。
拓扑排序的基本原理是从一个有向图开始,把它分解成多个有向图的子图,而每个子图只有一个顶点没有出度。
从其中一个没有出度的顶点开始,把它加入到一个结果序列,接着从它的邻节点中选择一个没有出度的顶点,加入到结果序列中,直到所有顶点加入到结果序列中,就得到了一个有序的拓扑排序结果。
在有向图的拓扑排序中,有若干种算法,如拓扑排序算法、Kahn 算法、深度优先搜索算法等,它们经常用来在有向图中完成拓扑排序。
拓扑排序算法要求每个节点拥有一个入度(可以理解为前驱节点的个数),会用入度为0的节点来构成起点,然后按照节点的入度从小到大的顺序搜索,直到所有节点都被搜索。
Kahn算法与拓扑排序算法类似,它使用一个队列,从0入度的节点开始,把所有的0入度的节点入队,然后按照它们的出度,从队列里取出每一个节点,把节点的所有后继节点减1入度,如果减完之后发现有入度为0的节点,那么就入队,直到队列为空,就可以完成拓扑排序。
深度优先搜索算法也可以用来进行拓扑排序,它的基本思想是,从一个节点开始,按照深度优先搜索的方式,把节点进行拓扑排序,直到所有节点都被搜索完毕,就可以得到一个拓扑排序的序列。
在实际应用中,拓扑排序经常被用来解决活动安排问题和调度问题,因为这些问题都可以抽象成图模型来表示,而图的拓扑排序可以由此得到一个有序的序列,使得它们能够在正确的时序内完成。
图的遍历:拓扑排序(+反向拓扑)
图的遍历:拓扑排序(+反向拓扑)【需要解决的问题】对⼀个有向⽆环图(DAG)拓扑排序。
拓扑排序就是,对图上的点进⾏排序,使得排在后⾯的点不能通过⼀条路径到前⾯的点。
⽐如下⾯这个图:其中⼀个拓扑排序是1,2,3,4,5;⽽1,2,5,3,4就不⾏,因为3能到达5。
因此可以得出:⽤⼀个队列实现,先把⼊度为0的点放⼊队列,每次将队⾸元素加⼊ans[i…j]中,删除队⾸元素,并删除该点连接的所有边,于是每次删除⼀个点都会产⽣⼀个新的⼊度为0的点,再把这个点插⼊队列,递归即可。
拓扑排序模板如下://复杂度:O(N+M),N为点数,M为边数//输⼊:n,vector变量g[] n表⽰点的个数,g[i][j]表⽰从点i连出去到点j的边,有g[i][j]=j;//输出:返回对给定的图,是否能够拓扑排序;L[]⽤来记录拓扑排序的结果const int MAXN=100005;vector<int> g[MAXN];int degree[MAXN],L[MAXN],n,m;bool toposort(){memset(degree,0,sizeof(degree));for(int i=0;i<n;i++)for(int j=0;j<g[i].size();j++)degree[g[i][j]]++;int tot=0;queue<int> que; //tuposort的实现类似于BFSfor(int i=0;i<n;i++)if(!degree[i])que.push(i);while(!que.empty()){int x=que.front();que.pop();L[tot++]=x;for(int j=0;j<g[x].size();j++){int t=g[x][j];degree[t]--;if(!degree[t])que.push(t);}}if(tot==n) return true;return false;}但实际上,在⼀个图的拓扑排序的问题中,因为常常有不⽌⼀种排序⽅式,所以题⽬往往要求在所有排序⽅式中,让序号⼩的尽量排前(只要满⾜拓扑条件),所以⽤queue不够,得⽤优先队列priority_queue来解决;⽽且,⽤优先队列实现时,不是把⼊度为0的点先放⼊队列,⽽是出度为0的点先⼊队(反向拓扑);拓扑排序+优先队列模板如下:const int MAXN=100005;vector<int> g[MAXN];int degree[MAXN],L[MAXN],n,m;void toposort(){int tot=0;priority_queue<int> que; //toposort的实现类似于BFSfor(int i=1;i<=n;i++)if(!degree[i])que.push(i);while(!que.empty()){int x=que.top();que.pop();L[tot++]=x;for(int j=0;j<g[x].size();j++){int t=g[x][j];degree[t]--;if(!degree[t])que.push(t);}}// if(tot==n) return true;// return false;}inline void init_input(int m){memset(degree,0,sizeof(degree)); memset(L,0,sizeof(L));memset(g,0,sizeof(g));for(int i=0;i<m;i++){scanf("%d %d",&a,&b);g[b].push_back(a);degree[a]++; //important}}。
基于dag的拓扑排序改进算法及在bom中的运用
基于dag的拓扑排序改进算法及在bom中的运用一、拓扑排序改进算法拓扑排序(Topological Sorting)是一种可以用来表示有向无环图(DAG, Directed Acyclic Graph)的有序列表,其中每个顶点都按拓扑顺序排列。
在拓扑排序中,我们必须将每个节点排在任何其他相连节点之前,以防止循环出现在有向图中。
拓扑排序的主要用途是解决计算机中的依赖性问题,例如工作表中的公式,A概念包含B概念,B概念由C 概念给出,因此我们需要先了解C概念,再去掌握B概念,最后才能理解A概念。
在常规情况下,拓扑排序从顶点搜索出发,当发现一个没有入度的顶点时,把它放到一个列表(称为可安排列表)里去。
之后,把由该顶点出发的边从图中去掉。
在图中,剩下的边可能会有没有入度的顶点,把它们放到可安排列表里,再把由它们出发的边从图中去掉,重复上述操作,直到可安排列表中存在节点为止,最后把列表中存在的节点排序输出,即可获得一个有序的顶点列表。
但是,由于这种算法需要逐渐地从有向图中删除顶点和边,所以它的时间复杂度为O(V2+E),其中V为顶点的个数,E为边的个数,这显然是非常慢的,对大型有向图来说,拓扑排序的效率就很差了。
为了解决这种效率问题,研究者提出了一种基于DAG的拓扑排序改进算法——“拓扑排序可以按照任意设定的优先级进行处理”。
该算法实际上是一种类似于深度优先搜索(DFS)的算法,但它在深度优先搜索同时进行子节点扫描时根据节点任务或者优先级的设置进行调整。
这种改进的算法可以大大提高拓扑排序的效率,把图的时间复杂度从O(V2+E)降低到O(V+E)。
同时,它也为拓扑排序的应用范围打开了新的可能,比如在多核系统中的应用,可以在多核处理器上更加有效地利用拓扑排序算法。
二、基于拓扑排序改进算法在BOM中的运用BOM(Bill Of Material)是在制造行业中常用的一种产品清单,它包含了完整的产品结构图和详细的产品组件、零配件列表等,能够指导产品制造、装配和全过程管理。
拓扑排序的应用解决课程安排和任务调度问题
拓扑排序的应用解决课程安排和任务调度问题拓扑排序是图论中的一种排序算法,通过分析有向无环图(DAG)中各个顶点之间的依赖关系,可以找到满足依赖关系的顺序。
该算法在解决课程安排和任务调度问题方面有着广泛的应用。
一、课程安排问题在学校的课程安排中,课程之间存在先后依赖关系,比如某些课程必须在先修课程完成后才能进行。
利用拓扑排序便可以解决这一问题。
以某大学为例,假设该学期有n门课程,其中一些课程存在先修关系。
首先,根据先修关系,我们可以构建一个有向图,其中每个课程对应一个顶点,而依赖关系对应的有向边。
接下来,我们可以使用拓扑排序来确定课程的学习顺序。
具体步骤如下:1. 创建一个队列,用于存储入度为0的顶点。
2. 遍历图中的所有顶点,将入度为0的顶点全部入队。
3. 当队列非空时,执行以下操作:- 出队一个顶点,输出该顶点作为当前学习的课程。
- 更新与该顶点相邻的顶点的入度,即将其入度减1。
- 若相邻顶点的入度减为0,则将其入队。
4. 若所有顶点都已输出,则拓扑排序成功;否则,存在环路,无法进行拓扑排序,即无法完成课程安排。
通过上述算法,我们可以得到一个满足课程依赖关系的学习顺序,使得学生按照该顺序完成所有课程的学习。
这种方法可以有效地解决课程安排问题,确保学生学习的合理性和顺序性。
二、任务调度问题在实际工作中,任务之间往往存在一定的依赖关系,比如某些任务必须在其他任务完成后才能开始。
利用拓扑排序可以有效解决任务调度问题。
假设我们需要完成一个项目,项目中有n个任务,其中某些任务存在依赖关系。
为了合理安排任务的执行顺序,我们可以采用拓扑排序来解决。
首先,我们可以根据任务之间的依赖关系构建一个有向图。
其中每个任务对应一个顶点,而依赖关系对应的有向边。
接下来,我们可以使用拓扑排序来确定任务的执行顺序。
具体步骤如下:1. 创建一个队列,用于存储入度为0的顶点。
2. 遍历图中的所有顶点,将入度为0的顶点全部入队。
3. 当队列非空时,执行以下操作:- 出队一个顶点,执行该顶点对应的任务。
拓扑排序算法题
拓扑排序算法题
题目:拓扑排序
给定一个有向图,请输出其拓扑排序结果。
拓扑排序是一种对有向无环图(DAG)进行排序的算法,它会生成一个线性序列,使得对于任何从顶点 u 到顶点 v 的有向边 uv,u 在排序中都出现在 v 之前。
输入格式:
第一行包含两个整数 n 和 m,分别表示有向图中的顶点数和边数。
接下来 m 行,每行包含两个整数 u 和 v,表示一条从顶点 u 到顶点 v 的有向边。
输出格式:
输出一行,包含 n 个整数,表示拓扑排序的结果。
如果有多组解,则输出任意一组即可。
数据范围:
1 <= n <= 1000
1 <= m <= 20000
样例输入:
5 5
1 2
2 3
3 4
4 5
5 1
样例输出:
1 2 3 4 5
解题思路:
拓扑排序算法的思路是不断从有向图中删除入度为 0 的顶点,并将其加入结果序列中,直到所有顶点都被删除或发现有环为止。
因此,我们可以使用队列来实现该算法。
具体步骤如下:
1. 创建一个空队列和一个空结果序列。
2. 遍历所有顶点,将入度为 0 的顶点加入队列中。
3. 从队列中取出一个顶点,将其加入结果序列中,并将其从有向图中删除。
同时,减小与其相连的所有顶点的入度,并将入度变为0 的顶点加入队列中。
4. 重复步骤 3 直到队列为空或发现有环为止。
5. 如果结果序列中的顶点个数等于有向图中的顶点个数,则输出结果序列;否则说明有环,无法进行拓扑排序。
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
目录一、系统开发的背景 (1)(一)问题描述 (1)(二)任务要求 (1)(三)测试数据 (2)(四)系统模块结构设计 (2)三、系统的设计与实现 (3)(一)系统流程图: (3)(二)主函数模块 (4)(三)图存储结构的建立 (4)四、系统测试 (8)(一)测试界面选择的实现 (8)(二)测试拓扑排序的实现 (8)(三)测试关键活动的实现 (8)五、总结 (9)六、附件(代码、部分图表) (10)拓扑排序一、系统开发的背景为了在科技不断进步的今天能够紧跟着时代的步伐,人们开始不断地追求方便和快捷的生活方式,在这个新鲜的时代,越来越多的新鲜事物层出不穷,为人们提供着各种方便和便利,为了更好地适应和接受这个时刻进步着的社会,我们要努力赶上。
在实际工作中,经常要使用一个有向图来表示工程的施工流程或者产品生产的流程图。
也就是说,一个大的工程经常被划分为若干个较小的子工程,这些子工程称为“活动”(Activity)。
当这些子工程全部完成时,整个工程也就完成了。
并且更要关心整个工程完成的最短时间,这就是有向权图的另一个重要应用——工程进度的关键路径问题。
用图的邻接表(出边表)表示方法,实现拓扑排序和关键路径的求解过程。
二、系统分析与设计(一)问题描述拓扑排序可判断AOV网络中是否存在回路,使得所有活动可排成一个线性序列,使用每个活动的所有前驱活动都排在该活动的前面。
关键路径的工期决定了整个项目的工期。
任何关键路径上的终端元素的延迟将直接影响项目的预期完成时间(例如在关键路径上没有浮动时间)。
(二)任务要求构建AOV网络,并输出其拓扑序列结果,输出该图的关键路径和关键活动,存储结构自行选择。
(三)测试数据自行设定(结点数不少于10个)。
(四)系统模块结构设计分析:此问题实现需采用拓扑排序和关键路径实现AOV网的拓扑排序和AOE网的关键路径求法,而在求关键路径过程中又用到了拓扑排序来判断图中是否存在回路。
为了保持图的初始化一致性,虽然在拓扑排序算法中不需要求边的权值,但关键路径需要。
所以在输入图的时候都规定了一种格式:(v1,v2,w1),表示边(v1,v2)上的权值为w1。
通过对系统功能的分析,拓扑排序的实现功能如图1所示。
图1拓扑排序的实现功能图通过上图的功能分析,把整个系统划分为4个模块:1、完成对图的建立,该模块主要实现:完成数据的存储,并将数据引入AOV网中;2、AOE网的拓扑排序,该模块主要实现:使得所有活动可排成一个线性序列,使用每个活动的所有前驱活动都排在该活动的前面进行排序;3、 AOE网的关键路径,该模块主要实现:找出众多活动中的某条具体关键路径;4、实现对结果的正确输出,该模块主要实现:对结果的正确输出与检测;三、系统的设计与实现(一)系统流程图:(二)主函数模块main函数首先调用SqStack ToPoSort;SqStack ToPoReverseSort;函数来定义栈,调用InitStack(ToPoSort);来初始化存拓扑排序的栈和InitStack(ToPoReverseSort);来初始化逆拓扑排序的栈。
其次调用CreateALGraph(ALGraph &graph)函数定义和初始化AOE网,调用TopologicalSor t(ALGraph &graph,SqStack &ToPoReverseSort) 函数求拓扑序列和调用PutInfoToPoSort(ToPoSort,graph);函数来输出输出拓扑顺序排序。
然后调用GetVeAndVl(graph,ToPoSort,ToPoReverseSort) 函数求结点和活动的最晚开始时间、最早开始时间并输出。
最后调用Status CriticalPath(ALGraph &graph,SqStack RevSort)函数来求关键活动、关键事件并输出。
(三)图存储结构的建立1. 先建立邻接表的存储单元,为建立邻接表做准备为图中每个顶点建立一个单链表,第i个单链表中的结点表示依附于顶点vi的边(对于有向图是以vi为尾的弧)。
每个结点由3个域组成,其中邻接域(adjvex)指示与顶点vi邻接的点在图中的位置,链域(nextedge)指示下一条边或弧的结点,权值域(W)存储边或弧的权值大小。
在表头结点除了设有链域(firstedge)指向链表中第一个结点之外,还设有存储顶点v或其他有关的数据域(data)和存储顶点入度的域(id)(代码如下)。
typedef struct node {int adjvex;int w;struct node *nextedge;}edgenode;typedef struct {char data;int id;edgenode *firstedge;}vexnode;2. 然后构造有向图第一,输入顶点信息存储在顶点表中,并初始化该顶点的便表。
第二,首先输入边所依附的两个顶点的序号i和j然后生成新的邻接点序号为j 的边表结点,最后将该结点插入到第i个表头部。
(代码如下)for(int k=0;k<arcnumber;k++) {scanf("%d,%d,%d",&begin,&end,&duttem);p=(edgenode*)malloc(sizeof(edgenode));p->adjvex =end-1;p->w =duttem;Graph[end-1].id ++;p->nextedge =Graph[begin-1].firstedge ;Graph[begin-1].firstedge =p;3.求取关键路径利用AOE网进行工程管理时,需解决的两个主要问题:其一,计算完成整个工程的最短工期;其二,确定关键路径,以找出哪些活动时影响工程进度的关键。
因此须计算以下几点:(1)事件的最早发生时间ve[k];(2)事件最迟发生时间vl[k];(3)活动最早开始时间ee[i];(4)活动的最迟开始时间el[i];计算其过程必须分别在拓扑有序和逆拓扑有序的前提下进行。
也就说,ve[k]必须在事件vk所有前驱的最早发生的时间求得之后才能确定。
因此,可以在拓扑排序的基础上计算ve[k]和vl[k]。
由此得到求解关键路径的方法:首先输入e条有向边<i,j>,建立AOE网的邻接表存储结构;然后从始点出发,令事件的最早发生时间为0,按拓扑有序求其余各顶点时间的最早发生时间ve[k];(代码如下)while(p) {k=p->adjvex ;Graph[k].id --;if(ve[j]+p->w >ve[k])ve[k]=ve[j]+p->w ;}接着从终点出发,令事件最迟发生时间等于其最早发生时间,按你你逆拓扑排序求其余各顶点事件最迟发生时间 vl[k];最后根据各顶点事件的ve和vl值,求所有活动最早开始时间ee和最迟开始时间el。
如果某活动满足条件ee=el,则为关键活动。
(代码如下)if(el[i]==ee[i]) {printf(" 此弧为关键活动 ");}同时,为计算各顶点事件的ve值是在拓扑排序的过程中进行的,因此需一个队列来记录拓扑排序,如果顶点的入度为0,则该顶点从队尾进入队列,拓扑排序时,从队头出队列。
if(Graph[k].id ==0)topology_queue[++rear]=k;p=p->nextedge ;四、系统测试(一)测试界面选择的实现调试过程如下:调试结果与预期结果相同。
该程序正确。
(二)测试拓扑排序的实现调试过程如下:调试结果与预期结果相同。
该程序正确。
(三)测试关键活动的实现调试过程如下:调试结果与预期结果相同,该程序正确。
五、总结系统完成了对图的建立,并且能够求出有向图的拓扑排序,实现最短路径等功能。
系统有不能完整的求出关键活动。
我的收获:通过这次课程设计,使我对C语言有了更进一步的认识和了解,要想学好它要重在实践,要通过不断的上机操作才能更好地学习它,我也发现我的好多不足之处。
首先是自己在电脑操作上还不熟悉,经常出错,通过学习也有所改进。
所以后在学习过程中,我会更加注视实践操作,使自己便好地学好计算机。
六、附件(代码、部分图表)#include<iostream>#include<stdio.h>#include<conio.h>#include<malloc.h>#define MAX_VEXTEX_NUM 40using namespace std;typedef struct edgenode{int endvex; /*相邻顶点在顶点表中的下标*/int weight; /*边的权值*/struct edgenode *nextedge; /*链字段*/}EdgeNode,*EdgeList; /*边表中的结点*/typedef struct{int vertex; /*顶点*/EdgeList edgelist; /*边表头指针*/}VexNode,AdjList[MAX_VEXTEX_NUM]; /*顶点表中的结点*/typedef struct{int vexnum; /*图的顶点数*/int arcnum; /*图的边的个数*/AdjList vexs; /*顶点表*/} GraphList; /*图的邻接表表示法*/void FindInDegree(GraphList *G,int *indegree);int TopoSort(GraphList *G,int *ptopo); /*拓扑排序*/int CriticalPath(GraphList *G) ; /*关键路径*//*关键路径*/int CriticalPath(GraphList *G){int i,j,k,sum=0;EdgeList p;int *ee=(int *)malloc(sizeof(int)*G->vexnum);int *le=(int *)malloc(sizeof(int)*G->vexnum);int *l=(int *)malloc(sizeof(int)*G->vexnum);int *e=(int *)malloc(sizeof(int)*G->vexnum);int *topo=(int *)malloc(sizeof(int)*G->vexnum);if(TopoSort(G,topo)==0){printf("该AOV网有环!\n");getch();return(0);}/*求事件可能的最早发生时间*/for(i=0; i<G->vexnum; i++)ee[i]=0;for(k=0; k<G->vexnum; k++){i=topo[k];p=G->vexs[i].edgelist;while(p!=NULL){j=p->endvex;if(ee[i]+p->weight>ee[j])ee[j]=ee[i]+p->weight;p=p->nextedge;}}sum=ee[G->vexnum-1]; /*工程的最短完成时间*/for(i=0; i<G->vexnum; i++) /*求事件允许的最迟发生时间*/le[i]=ee[G->vexnum-1];for(k=G->vexnum-2; k>=0; k--){i=topo[k];p=G->vexs[i].edgelist;while(p!=NULL){j=p->endvex;if((le[j]-p->weight)<le[i])le[i]=le[j]-p->weight;p=p->nextedge;}}k=0;printf("\n关键路径:\n");printf("各活动的最早发生时间依次为:\n");for(int q=0;q<G->vexnum;q++)printf("%d' '\n",ee[q]);printf("各活动的最晚发生时间依次为:\n");for(q=0;q<G->vexnum;q++)printf("%d' '\n",le[q]);/*求活动 ak 的最早开始时间 early(k)和最晚开始时间 late(k)*/printf("\n| 活动 | 最早 | 最晚 | 差值 | 是否关键 ? \n");for(i=0;i<G->vexnum;i++){p=G->vexs[i].edgelist;while(p!=NULL){j=p->endvex;e[k]=ee[i];l[k]=le[j]-p->weight;printf("| <%d,%d> | %4d | %4d | %4d |",i,j,e[k],l[k],l[k]-e[k]);if(e[k]==l[k])printf(" YES"); /*输出是否关键*/elseprintf(" NO");printf("\n");k++;p=p->nextedge;}}printf("\n最短完成时间: %d\n",sum); /*最短完成时间*/printf("\n");getch();return(1);}/*初始化图*/void InitGraph(GraphList *G){int i,vexnum,arcnum,weight=0;int v1,v2;EdgeList p;printf("请输入顶点数和边数-->(如:x,y)\n"); /*输入顶点数和边数*/scanf("%d,%d",&vexnum,&arcnum); /*输入注意逗号隔开*/G->vexnum=vexnum;G->arcnum=arcnum;for(i=0;i<vexnum;i++){G->vexs[i].vertex=i+1;G->vexs[i].edgelist=NULL;}for(i=0;i<arcnum;i++){printf("请输入各个连接点及其权值-->(如: 1,2,10 )\n",i+1); /*输入各个连接点及其权值*/scanf("%d,%d,%d",&v1,&v2,&weight); /*输入注意逗号隔开*/if(v1>G->vexnum||v2>G->vexnum){printf("你输入的节点数不符合条件!!"); /*判断输入点是否复合条件*/printf("\n");getch();exit(0);}p=(EdgeList)malloc(sizeof(EdgeNode));p->endvex=v2;p->weight=weight;p->nextedge=G->vexs[v1].edgelist;G->vexs[v1].edgelist=p;}}/*拓扑排序*/int TopoSort(GraphList *G,int *ptopo){EdgeList p;int i,j,k,nodeno=0,top=-1;int *indegree=(int *)malloc(sizeof(int)*G->vexnum);FindInDegree(G,indegree); /*indegree 数组赋初值*/for(i=0; i<G->vexnum; i++) /* 将入度为零的顶点入栈*/if(indegree[i]==0){ /*静态链式栈*/indegree[i]=top;top=i;}while(top!=-1){j=top;top=indegree[top]; /*取当前栈顶元素并退栈*/ptopo[nodeno++]=j; /*将该顶点输出到拓扑序列中*/p=G->vexs[j].edgelist; /*取该元素边表中的第一个边结点*/while(p){k=p->endvex;indegree[k]--; /*删除以该顶点为起点的边*/if(indegree[k]==0){indegree[k]=top; /*将新的入度为零的顶点入栈*/top=k;}p=p->nextedge;}}free(indegree);if(nodeno<G->vexnum)return(0); /*AOV 网中存在回路*/elsereturn(1);}/*求出图中所有顶点的入度*/void FindInDegree(GraphList *G,int *indegree){int i;EdgeList p;for(i=0; i<G->vexnum; i++)indegree[i]=0;for(i=0; i<G->vexnum; i++){p=G->vexs[i].edgelist;while(p){++indegree[p->endvex];p=p->nextedge;}}}void TopoSortMenu(void){int *ptopo;int i;GraphList *Graph=(GraphList *)malloc(sizeof(GraphList));InitGraph(Graph);ptopo=(int *)malloc(sizeof(int)*Graph->vexnum);if(TopoSort(Graph,ptopo)!=0){printf("\n拓扑排序:\n");for(i=0;i<Graph->vexnum-1;i++)printf("v%d-->",ptopo[i]); /* 打印前 n-1 个 ( 有 -->)*/ printf("v%d",ptopo[i]); /*打印最后一个(没有-->)*/printf("\n\n");}elseprintf("该AOV网有环!\n");getch();free(ptopo);free(Graph);}void CriticalMenu(void){GraphList *Graph=(GraphList *)malloc(sizeof(GraphList));InitGraph(Graph);CriticalPath(Graph);free(Graph);}void TopoCriticalMenu(void){char ch;while(1){printf("***************《功能菜单》****************\n");printf(" 请选择: \n");printf(" 1.拓扑排序 \n");printf(" 2.关键路径 \n");printf(" 0.退出 \n");printf("*******************************************\n");ch=getch();switch(ch-'0'){case 0: exit(0);case 1: TopoSortMenu(); break;case 2: CriticalMenu(); break;}}}void main(void){TopoCriticalMenu();}。