1、数据结构关键路径及十字链表基本操作课程设计报告数据结构课程设计报告 小组成员:王瑞琦 13052007姜薇 13052011刘倩 13052027小组课题: 1-1有向图十字链表的操作2-5 关键路径 有向图十字链表的操作功能概述将有向图以是十字链表的形式存储,并借助十字链表对有向图进行查找有向图中指定结点的度(出度和入度)、插入有向边和删除有向边等操作。模块简介创建有向图十字链表:void creat_crosslink(ALGraph *&G,char vex,int n,etype edge,int e)查找结点在十字链表中的位置:int LocateVex(ALGraph *G,Ver
2、tex u) 插入指定边:bool InsertArc(ALGraph *G,etype a)删除指定边:bool DeletetArc(ALGraph *G,etype a)查找有向图中指定结点的度(出度和入度):int Count(ALGraph *G,Vertex u)数据类型/*边的数据类型*/typedef struct char vi,vj; int info; etype;/*弧结点数据类型*/typedef struct ArcNode int tailvex,headvex;/* 该弧的尾和头顶点的位置 */ struct ArcNode *hlink,*tlink;/* 分
3、别为弧头相同和弧尾相同的弧的链域 */ InfoType info;/*若为网络则为权值*/ArcNode;/*表头结点数据类型*/typedef struct VexNode Vertex data; ArcNode *firstin,*firstout; /* 指向该顶点的第一条入弧和出弧 */VexNode;typedef VexNode AdjListMAXV;/*十字链表数据类型*/typedef struct AdjList adjlist; int n,e;/*图中顶点数n和边数e*/ALGraph;概要设计CreateDG(建表)(1)获取有向图的顶点数、弧数并存入;(2)依次
4、获取各顶点的值,存入数组,构建十字链表头结点向量组;(3)依次获取弧信息,存入,确认弧结点在十字链表中的位置并对弧赋值;(4)十字链表创建成功;LocateVex(查找)传参:有向图,顶点利用指针依次扫描表头数组,当找到该顶点时,返回该顶点在十字链表表头数组的位置(序号),未找到该顶点时,返回-1。Count(求度)传参:有向图,顶点调用LocateVex(),找到顶点的位置,利用tlink指针依次扫描以该顶点为弧尾的边,统计该顶点的出度,利用hlink指针依次扫描以该顶点为弧头的边,统计该顶点的入度,那么该顶点的度为入度加出度。InsertArc(插入边)传参:有向图,边调用LocateVe
5、x(),找到顶点的位置,建立新的存储空间,对新存储空间赋值,通过修改tlink和hlink,将新结点放入十字链表。bool DeletetArc(ALGraph *G,etype a)传参:有向图,边调用LocateVex(),找到顶点的位置,通过修改指针,将指定的边结点从十字链表中删除,同时释放存储空间。详细设计(源代码)/*建立十字链表*/void creat_crosslink(ALGraph *&G,char vex,int n,etype edge,int e) ArcNode *p; G=(ALGraph *)malloc(sizeof(ALGraph); G-n=n;G-e=e;
6、 int k,i,j; for (i=0;iadjlisti.firstin=NULL; G-adjlisti.firstout=NULL; G-adjlisti.data=vexi; for(k=0;kadjlisti.data !=edgek.vi) i+; j=0; while(G-adjlistj.data !=edgek.vj) j+; p=(ArcNode *)malloc(sizeof(ArcNode); p-headvex=i+1;/始边顶点(数组元素第一个序号为0) p-tailvex=j+1;/终边顶点 p-info=edgek.info;/权值 p-hlink=G-adj
7、listi.firstout;/建立弧头链表 p-tlink=G-adjlistj.firstin;/建立弧尾链表 G-adjlisti.firstout=G-adjlistj.firstin=p;/头插 /*查找指定顶点的位置(序号)*/int LocateVex(ALGraph *G,Vertex u) int i; for(i=0;i(*G).n;i+)/循环查找结点 if(*G).adjlisti.data=u) return i;/如果找到该结点就返回其在十字链表中的序号 return -1;/如果没有找到该结点,返回-1/*求指定顶点的度*/int Count(ALGraph *G
8、,Vertex u) int i=0,j=0,k; ArcNode *p,*q; k=LocateVex(G,u);/k是顶点v的序号 if(ktlink!=NULL)/依次扫描以该顶点为尾结点的弧 p=p-tlink; i+; while(q!=NULL & q-hlink=NULL)/依次扫描以该顶点为头结点的弧 q=q-hlink; j+; return i+j;/*插入新边*/bool InsertArc(ALGraph *G,etype a) int i,j; ArcNode *p; i=LocateVex(G,a.vi);/弧尾序号 j=LocateVex(G,a.vj);/弧头序
9、号 if(i0|jtailvex=i;/给新结点赋值 p-headvex=j; p-info=a.info; p-hlink=(*G).adjlistj.firstin;/插在入弧和出弧的链头 p-hlink=(*G).adjlisti.firstout; (*G).adjlistj.firstin=(*G).adjlisti.firstout=p; (*G).e+;/弧数+1 return true;/*删除指定边*/bool DeletetArc(ALGraph *G,etype a) int i,j; ArcNode *p1,*p2; i=LocateVex(G,a.vi);/弧尾序号
10、j=LocateVex(G,a.vj);/弧头序号 if(i0|jheadvex=j)/第一个结点为待删除结点 (*G).adjlisti.firstout=p2-tlink; else while(p2&p2-headvex!=j) p1=p2; p2=p2-tlink; if(p2)/没到表尾 p1-tlink=p2-tlink; p2=(*G).adjlistj.firstin;/将弧结点从入弧链表中删去 if(p2&p2-tailvex=i) (*G).adjlistj.firstin=p2-hlink; else while(p2&p2-tailvex!=i) p1=p2; p2=p
11、2-hlink; if(p2)/没到表尾 p1-hlink=p2-hlink; if(p2-info)/释放弧结点 free(p2); (*G).e-;/弧数-1 return true;调试分析及问题解决1、typedef struct OLGArc,定义弧结点结构体,包含:两个整数类型的数据:tailvex、headve,q,分别为该弧的尾顶点、头顶点和该弧的权值;两个指针:*hlink、*tlink,分别为与该弧拥有相同头顶点的链域和与该弧拥有相同尾顶点的链域;2、typedef struct OLGVNode,定义顶点结点结构体,包含:一个字符类型的数据:data,为顶点的名字;两个指
12、针:*firstin,*firstout,分别指向该顶点的第一条入弧和出弧;3、typedef struct ALGraph,定义十字链表结构体,包含:一个OLGVNode类型的数组,用于存放表头向量;两个整数类型的数据:vexnum、arcnum,分别为有向图的当前顶点数和弧数;4、Status CreateDG(ALGraph *G),创建十字链表的函数,实现创建空的十字链表,在十字链表中放入有向图的顶点、弧、权值等功能;5、void DestroyGraph(ALGraph *G),销毁十字链表的函数,实现销毁用十字链表存储的有向图的功能;6、VertexType* GetVex(ALG
13、raph G,int v),查找序号为v的顶点并返回该顶点的名字(即该顶点的值);7、建表时为了统一代码,由另一人更改了建表的操作,导致建表时从1开始计数而不是0,后面的查找和删除功能有误;更改后正常。8、在插入指定边时,插入边将原有的边覆盖;在查找指定结点的度时,获得的结果不正确,特别是在插入一条新边后的数据与原数据的差不为1。解决办法:通过调试查找,找到了错误来源:1、在创建十字链表时,数据的下标是从1开始存储的,而在“查找指定结点的度”、“插入边”、“删除边”等函数中均默认十字链表的存储下标是从0开始;2、在创建十字链表时,出现弧头和弧尾的定义与函数中的定义相反(如下图说明)。于是进行如
14、下修改:1、将创建十字链表函数中的下标改为从0开始存储;2、将“查找指定结点的度”、“插入边”、“删除边”等函数出现的弧头相关信息改为弧尾的,将弧尾相关信息改为弧头的。再次经过调试,证明程序正确。关键路径功能概述输入有向图,求其关键路径及最短时间。模块简介创建有向图邻接表;creatlink(ALGraph *&G,char vex,int n,etype edge,int e)输入有向图邻接表;Inputgraph(ALGraph *&G)获取事件发生最早时间;int get_ve(ALGraph *G,int i,VNode q)获取时间发生最迟时间;int get_vl(ALGraph
15、*G,int i,VNode q)对有向图进行拓扑排序;TopSort(ALGraph *&G)求关键活动及最短时间。get_keyroad(ALGraph *G)数据类型/*边的数据类型*/typedef struct char vi,vj; int info;etype;/*边结点数据类型*/typedef struct ANode int adjvex; /*邻接点存储序号*/ int lt;/*活动最迟发生时间*/ int flag;/*标记是否为关键活动*/ InfoType info; /*若是网络存储权值*/ struct ANode *nextarc; /*指向下一个边结点*/
16、ArcNode;/*表头结点数据类型*/typedef struct Vertex data; /*存储顶点元素*/ int count;/*顶点入度*/ int ve,vl;/*事件最早及最迟发生时间*/ ArcNode *firstarc; /*指向依附于该顶点的第一边*/VNode;typedef VNode AdjListMAXV;/*邻接表数据类型*/typedef struct AdjList adjlist; int n,e;/*图中顶点数n和边数e*/ALGraph;概要设计Creatlink(建表)此函数用于调试后面的功能传参:空表,顶点,顶点个数,边数,有向边信息邻接表为链
17、式存储,传参使用的数组则为顺序存储。步骤为:1.将顶点信息(char vex)放入表头结点(G-adjlisti.data);2.将有向边信息(etype edge)填入表中作为边结点。为了方便调试采用尾插法,后期可能会改成头插。Inputgraph(输入图)此函数为正式建表传参:空的有向图步骤为:先输入顶点数和边数,确定邻接表大小后再进行边信息的录入。输入顶点信息,建立邻接表头-输入边信息,将边信息填入表中作为边结点。为了便于调试采用尾插法。 TopSort(拓扑排序)传参:有向图(以邻接表方式存储)步骤为:利用栈存储入度为零的顶点,逐一输出,再删去其作为始点出发的边得出下一个入度为零的点,
18、输出,重复上述步骤,即得拓扑排序序列。一边进行拓扑排序的同时一边调用get_ve(图,头结点序号,头结点)函数获得每个事件发生的最早时间,同时将序列存入一个栈St1以便后面获得拓扑排序逆序列。由于St1属于本函数中的局部变量,因此get_vl函数放在本函数里,对St1进行出栈操作,获取拓扑排序逆序列并求算事件最迟发生时间。get_keyroad(关键路径及最短时间)传参:有向图(以邻接表方式存储)遍历有向图,利用公式活动最迟发生时间=终点事件最迟发生时间-活动持续时间活动最早发生时间=起点事件最早发生时间求出关键活动,以此获得关键路径及最短时间。详细设计(源代码)/*建立邻接表*/void c
19、reatlink(ALGraph *&G,char vex,int n,etype edge,int e) ArcNode *p,*t; t=(ArcNode *)malloc(sizeof(ArcNode); t-nextarc=NULL; G=(ALGraph *)malloc(sizeof(ALGraph); G-n=n;G-e=e; int k,i,j; for (i=0;iadjlisti.firstarc=NULL; G-adjlisti.data=vexi; G-adjlisti.ve=0; for(k=0;kadjlisti.data !=edgek.vi) i+; j=0;
20、while(G-adjlistj.data !=edgek.vj) j+; p=(ArcNode *)malloc(sizeof(ArcNode); p-adjvex=j;/终边顶点 p-info=edgek.info;/权值 p-flag=0;/默认为非关键活动 if(G-adjlisti.firstarc=NULL)/尾插 G-adjlisti.firstarc=p; p-nextarc=NULL; t=p; else t-nextarc=p; p-nextarc=NULL; t=p; /*输入邻接表*/void Inputgraph(ALGraph *&G) char e2; int l
21、,i,j,k=0;/l:边长 ArcNode *p,*t; t=(ArcNode *)malloc(sizeof(ArcNode); t-nextarc=NULL; G=(ALGraph *)malloc(sizeof(ALGraph); printf(please input vex and edge number:n);/先输入顶点数和边数 scanf(%d%d,&G-n,&G-e); for (i=0;in;i+)/*置入各表头结点(即顶点)*/ G-adjlisti.data=a+i;/自动生成顶点 G-adjlisti.firstarc=NULL; G-adjlisti.ve=0;
22、while(ke)/输入边信息 printf(please add edge information:n); scanf(%s,e); scanf(%d,&l); i=0; while(G-adjlisti.data !=e0) i+; j=0; while(G-adjlistj.data !=e1) j+; p=(ArcNode *)malloc(sizeof(ArcNode); p-adjvex=j;/终边顶点 p-info=l;/权值 p-flag=0;/默认为非关键活动 if(G-adjlisti.firstarc=NULL)/尾插 G-adjlisti.firstarc=p; p-n
23、extarc=NULL; t=p; else t-nextarc=p; p-nextarc=NULL; t=p; k+; /*获取事件发生最早时间*/int get_ve(ALGraph *G,int i,VNode q) int j=0; ArcNode *p; if(i=0) return 0; else while(jadjlistj.firstarc; if(p=NULL) j+; continue; while(p-nextarc!=NULL & p-adjvex!=i) p=p-nextarc; if(p-adjvex=i&p-info+G-adjlistj.ve=G-adjlis
24、ti.ve) G-adjlisti.ve=p-info+G-adjlistj.ve; j+; else j+; return G-adjlisti.ve; /*获取事件发生最迟时间*/int get_vl(ALGraph *G,int i,VNode q) ArcNode *p; p=G-adjlisti.firstarc; if(i=0) return 0; G-adjlisti.vl=G-adjlistp-adjvex.vl-p-info;/出度为0的顶点只有一个且已出栈,此处无需判断p!=NULL while(p-nextarc!=NULL) p=p-nextarc; if(G-adjl
25、isti.vl=G-adjlistp-adjvex.vl-p-info) G-adjlisti.vl=G-adjlistp-adjvex.vl-p-info; return G-adjlisti.vl;/*拓扑排序*/void TopSort(ALGraph *G) int i,j; int StMAXV,top=-1; ArcNode *p; for(i=0;in;i+)/*入度置为0*/ G-adjlisti.count=0; for(i=0;in;i+)/*求所有顶点的入度*/ p=G-adjlisti.firstarc; while(p!=NULL) G-adjlistp-adjvex
26、.count+;/终点入度+1 p=p-nextarc; for(i=0;in;i+)/*入度为0的顶点进栈*/ if(G-adjlisti.count=0) top+; Sttop=i; /*进行拓扑排序并将结果逐个输出*/ while(top-1)/栈不为空时进行循环 i=Sttop;top-; printf(%c,G-adjlisti); p=G-adjlisti.firstarc; while(p!=NULL) j=p-adjvex; G-adjlistj.count-; if(G-adjlistj.count=0) top+; Sttop=j; p=p-nextarc; /*关键路径及最短时间*/void get_keyroad(ALGraph *G) int i=0, stime=0; ArcNode *p; printf(%c,G-adjlisti); while(in) p=G-adjlisti.firstarc; while(p!=NULL ) p-lt=G-adjlistp-adjvex.vl-p-info; if(p-lt-G-adjlisti.ve=0) p-flag=
copyright@ 2008-2022 冰豆网网站版权所有
经营许可证编号:鄂ICP备2022015515号-1