当前位置:文档之家› 拓扑排序

拓扑排序

目录一、系统开发的背景 (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();}。

相关主题