拓扑排序

合集下载

dag 的拓扑排序

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算法题拓扑排序给定一个包含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)

图基本算法拓扑排序(基于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及拓扑排序

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```以上代码使用了一个字典来表示图的结构,其中键是节点名称,值是一个包含相邻节点的列表。

  1. 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
  2. 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
  3. 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();}。

相关文档
最新文档