数据结构关键路径及十字链表基本操作课程设计报告.docx
《数据结构关键路径及十字链表基本操作课程设计报告.docx》由会员分享,可在线阅读,更多相关《数据结构关键路径及十字链表基本操作课程设计报告.docx(18页珍藏版)》请在冰豆网上搜索。
数据结构关键路径及十字链表基本操作课程设计报告
数据结构课程设计报告
小组成员:
王瑞琦13052007
姜薇13052011
刘倩13052027
小组课题:
1-1有向图十字链表的操作
2-5关键路径
有向图十字链表的操作
功能概述
将有向图以是十字链表的形式存储,并借助十字链表对有向图进行查找有向图中指定结点的度(出度和入度)、插入有向边和删除有向边等操作。
模块简介
创建有向图十字链表:
voidcreat_crosslink(ALGraph*&G,charvex[],intn,etypeedge[],inte)
查找结点在十字链表中的位置:
intLocateVex(ALGraph*G,Vertexu)
插入指定边:
boolInsertArc(ALGraph*G,etypea)
删除指定边:
boolDeletetArc(ALGraph*G,etypea)
查找有向图中指定结点的度(出度和入度):
intCount(ALGraph*G,Vertexu)
数据类型
/*边的数据类型*/
typedefstruct
{charvi,vj;
intinfo;
}etype;
/*弧结点数据类型*/
typedefstructArcNode
{inttailvex,headvex;/*该弧的尾和头顶点的位置*/
structArcNode*hlink,*tlink;/*分别为弧头相同和弧尾相同的弧的链域*/
InfoTypeinfo;/*若为网络则为权值*/
}ArcNode;
/*表头结点数据类型*/
typedefstructVexNode
{Vertexdata;
ArcNode*firstin,*firstout;/*指向该顶点的第一条入弧和出弧*/
}VexNode;
typedefVexNodeAdjList[MAXV];
/*十字链表数据类型*/
{AdjListadjlist;
intn,e;/*图中顶点数n和边数e*/
}ALGraph;
概要设计
CreateDG(建表)
(1)获取有向图的顶点数、弧数并存入;
(2)依次获取各顶点的值,存入数组,构建十字链表头结点向量组;
(3)依次获取弧信息,存入,确认弧结点在十字链表中的位置并对弧赋值;
(4)十字链表创建成功;
LocateVex(查找)
传参:
有向图,顶点
利用指针依次扫描表头数组,当找到该顶点时,返回该顶点在十字链表表头数组的位置(序号),未找到该顶点时,返回-1。
Count(求度)
调用LocateVex(),找到顶点的位置,利用tlink指针依次扫描以该顶点为弧尾的边,统计该顶点的出度,利用hlink指针依次扫描以该顶点为弧头的边,统计该顶点的入度,那么该顶点的度为入度加出度。
InsertArc(插入边)
有向图,边
调用LocateVex(),找到顶点的位置,建立新的存储空间,对新存储空间赋值,通过修改tlink和hlink,将新结点放入十字链表。
调用LocateVex(),找到顶点的位置,通过修改指针,将指定的边结点从十字链表中删除,同时释放存储空间。
详细设计(源代码)
/*建立十字链表*/
{ArcNode*p;
G=(ALGraph*)malloc(sizeof(ALGraph));
G->n=n;G->e=e;
intk,i,j;
for(i=0;i{G->adjlist[i].firstin=NULL;G->adjlist[i].firstout=NULL;G->adjlist[i].data=vex[i];}for(k=0;k{i=0;while(G->adjlist[i].data!=edge[k].vi)i++;j=0;while(G->adjlist[j].data!=edge[k].vj)j++;p=(ArcNode*)malloc(sizeof(ArcNode));p->headvex=i+1;//始边顶点(数组元素第一个序号为0)p->tailvex=j+1;//终边顶点p->info=edge[k].info;//权值p->hlink=G->adjlist[i].firstout;//建立弧头链表p->tlink=G->adjlist[j].firstin;//建立弧尾链表G->adjlist[i].firstout=G->adjlist[j].firstin=p;//头插}}/*查找指定顶点的位置(序号)*/intLocateVex(ALGraph*G,Vertexu){inti;for(i=0;i<(*G).n;i++)//循环查找结点if((*G).adjlist[i].data==u)returni;//如果找到该结点就返回其在十字链表中的序号return-1;//如果没有找到该结点,返回-1}/*求指定顶点的度*/intCount(ALGraph*G,Vertexu){inti=0,j=0,k;ArcNode*p,*q;k=LocateVex(G,u);//k是顶点v的序号if(k<0)//k不是图G的顶点return-1;p=(*G).adjlist[k].firstin;//找出弧q=(*G).adjlist[k].firstout;//找入弧while(p!=NULL&&p->tlink!=NULL)//依次扫描以该顶点为尾结点的弧{p=p->tlink;i++;}while(q!=NULL&&q->hlink==NULL)//依次扫描以该顶点为头结点的弧{q=q->hlink;j++;}returni+j;}/*插入新边*/boolInsertArc(ALGraph*G,etypea){inti,j;ArcNode*p;i=LocateVex(G,a.vi);//弧尾序号j=LocateVex(G,a.vj);//弧头序号if(i<0||j<0)returnfalse;p=(ArcNode*)malloc(sizeof(etype));//生成新结点p->tailvex=i;//给新结点赋值p->headvex=j;p->info=a.info;p->hlink=(*G).adjlist[j].firstin;//插在入弧和出弧的链头p->hlink=(*G).adjlist[i].firstout;(*G).adjlist[j].firstin=(*G).adjlist[i].firstout=p;(*G).e++;//弧数+1returntrue;}/*删除指定边*/boolDeletetArc(ALGraph*G,etypea){inti,j;ArcNode*p1,*p2;i=LocateVex(G,a.vi);//弧尾序号j=LocateVex(G,a.vj);//弧头序号if(i<0||j<0||i==j)returnfalse;p2=(*G).adjlist[i].firstout;//将弧结点从出弧表中删去if(p2&&p2->headvex==j)//第一个结点为待删除结点(*G).adjlist[i].firstout=p2->tlink;else{while(p2&&p2->headvex!=j){p1=p2;p2=p2->tlink;}if(p2)//没到表尾p1->tlink=p2->tlink;}p2=(*G).adjlist[j].firstin;//将弧结点从入弧链表中删去if(p2&&p2->tailvex==i)(*G).adjlist[j].firstin=p2->hlink;else{while(p2&&p2->tailvex!=i){p1=p2;p2=p2->hlink;}if(p2)//没到表尾p1->hlink=p2->hlink;}if(p2->info)//释放弧结点free(p2);(*G).e--;//弧数-1returntrue;}调试分析及问题解决1、typedefstructOLGArc,定义弧结点结构体,包含:两个整数类型的数据:tailvex、headve,q,分别为该弧的尾顶点、头顶点和该弧的权值;两个指针:*hlink、*tlink,分别为与该弧拥有相同头顶点的链域和与该弧拥有相同尾顶点的链域;2、typedefstructOLGVNode,定义顶点结点结构体,包含:一个字符类型的数据:data,为顶点的名字;两个指针:*firstin,*firstout,分别指向该顶点的第一条入弧和出弧;3、typedefstructALGraph,定义十字链表结构体,包含:一个OLGVNode类型的数组,用于存放表头向量;两个整数类型的数据:vexnum、arcnum,分别为有向图的当前顶点数和弧数;4、StatusCreateDG(ALGraph*G),创建十字链表的函数,实现创建空的十字链表,在十字链表中放入有向图的顶点、弧、权值等功能;5、voidDestroyGraph(ALGraph*G),销毁十字链表的函数,实现销毁用十字链表存储的有向图的功能;6、VertexType*GetVex(ALGraphG,intv),查找序号为v的顶点并返回该顶点的名字(即该顶点的值);7、建表时为了统一代码,由另一人更改了建表的操作,导致建表时从1开始计数而不是0,后面的查找和删除功能有误;更改后正常。8、在插入指定边时,插入边将原有的边覆盖;在查找指定结点的度时,获得的结果不正确,特别是在插入一条新边后的数据与原数据的差不为1。解决办法:通过调试查找,找到了错误来源:1、在创建十字链表时,数据的下标是从1开始存储的,而在“查找指定结点的度”、“插入边”、“删除边”等函数中均默认十字链表的存储下标是从0开始;2、在创建十字链表时,出现弧头和弧尾的定义与函数中的定义相反(如下图说明)。于是进行如下修改:1、将创建十字链表函数中的下标改为从0开始存储;2、将“查找指定结点的度”、“插入边”、“删除边”等函数出现的弧头相关信息改为弧尾的,将弧尾相关信息改为弧头的。再次经过调试,证明程序正确。关键路径功能概述输入有向图,求其关键路径及最短时间。模块简介创建有向图邻接表;creatlink(ALGraph*&G,charvex[],intn,etypeedge[],inte)输入有向图邻接表;Inputgraph(ALGraph*&G)获取事件发生最早时间;intget_ve(ALGraph*G,inti,VNodeq)获取时间发生最迟时间;intget_vl(ALGraph*G,inti,VNodeq)对有向图进行拓扑排序;TopSort(ALGraph*&G)求关键活动及最短时间。get_keyroad(ALGraph*G)数据类型/*边的数据类型*/typedefstruct{charvi,vj;intinfo;}etype;/*边结点数据类型*/typedefstructANode{intadjvex;/*邻接点存储序号*/intlt;/*活动最迟发生时间*/intflag;/*标记是否为关键活动*/InfoTypeinfo;/*若是网络存储权值*/structANode*nextarc;/*指向下一个边结点*/}ArcNode;/*表头结点数据类型*/typedefstruct{Vertexdata;/*存储顶点元素*/intcount;/*顶点入度*/intve,vl;/*事件最早及最迟发生时间*/ArcNode*firstarc;/*指向依附于该顶点的第一边*/}VNode;typedefVNodeAdjList[MAXV];/*邻接表数据类型*/typedefstruct{AdjListadjlist;intn,e;/*图中顶点数n和边数e*/}ALGraph;概要设计Creatlink(建表)此函数用于调试后面的功能传参:空表,顶点,顶点个数,边数,有向边信息邻接表为链式存储,传参使用的数组则为顺序存储。步骤为:1.将顶点信息(charvex[])放入表头结点(G->adjlist[i].data);2.将有向边信息(etypeedge[])填入表中作为边结点。为了方便调试采用尾插法,后期可能会改成头插。Inputgraph(输入图)此函数为正式建表传参:空的有向图步骤为:先输入顶点数和边数,确定邻接表大小后再进行边信息的录入。输入顶点信息,建立邻接表头->输入边信息,将边信息填入表中作为边结点。为了便于调试采用尾插法。TopSort(拓扑排序)传参:有向图(以邻接表方式存储)步骤为:利用栈存储入度为零的顶点,逐一输出,再删去其作为始点出发的边得出下一个入度为零的点,输出,重复上述步骤,即得拓扑排序序列。一边进行拓扑排序的同时一边调用get_ve(图,头结点序号,头结点)函数获得每个事件发生的最早时间,同时将序列存入一个栈St1以便后面获得拓扑排序逆序列。由于St1属于本函数中的局部变量,因此get_vl函数放在本函数里,对St1进行出栈操作,获取拓扑排序逆序列并求算事件最迟发生时间。get_keyroad(关键路径及最短时间)传参:有向图(以邻接表方式存储)遍历有向图,利用公式活动最迟发生时间=终点事件最迟发生时间-活动持续时间活动最早发生时间=起点事件最早发生时间求出关键活动,以此获得关键路径及最短时间。详细设计(源代码)/*建立邻接表*/voidcreatlink(ALGraph*&G,charvex[],intn,etypeedge[],inte){ArcNode*p,*t;t=(ArcNode*)malloc(sizeof(ArcNode));t->nextarc=NULL;G=(ALGraph*)malloc(sizeof(ALGraph));G->n=n;G->e=e;intk,i,j;for(i=0;i{G->adjlist[i].firstarc=NULL;G->adjlist[i].data=vex[i];G->adjlist[i].ve=0;}for(k=0;k{i=0;while(G->adjlist[i].data!=edge[k].vi)i++;j=0;while(G->adjlist[j].data!=edge[k].vj)j++;p=(ArcNode*)malloc(sizeof(ArcNode));p->adjvex=j;//终边顶点p->info=edge[k].info;//权值p->flag=0;//默认为非关键活动if(G->adjlist[i].firstarc==NULL)//尾插{G->adjlist[i].firstarc=p;p->nextarc=NULL;t=p;}else{t->nextarc=p;p->nextarc=NULL;t=p;}}}/*输入邻接表*/voidInputgraph(ALGraph*&G){chare[2];intl,i,j,k=0;//l:边长ArcNode*p,*t;t=(ArcNode*)malloc(sizeof(ArcNode));t->nextarc=NULL;G=(ALGraph*)malloc(sizeof(ALGraph));printf("pleaseinputvexandedgenumber:\n");//先输入顶点数和边数scanf("%d%d",&G->n,&G->e);for(i=0;in;i++)/*置入各表头结点(即顶点)*/{G->adjlist[i].data='a'+i;//自动生成顶点G->adjlist[i].firstarc=NULL;G->adjlist[i].ve=0;}while(ke)//输入边信息{printf("pleaseaddedgeinformation:\n");scanf("%s",e);scanf("%d",&l);i=0;while(G->adjlist[i].data!=e[0])i++;j=0;while(G->adjlist[j].data!=e[1])j++;p=(ArcNode*)malloc(sizeof(ArcNode));p->adjvex=j;//终边顶点p->info=l;//权值p->flag=0;//默认为非关键活动if(G->adjlist[i].firstarc==NULL)//尾插{G->adjlist[i].firstarc=p;p->nextarc=NULL;t=p;}else{t->nextarc=p;p->nextarc=NULL;t=p;}k++;}}/*获取事件发生最早时间*/intget_ve(ALGraph*G,inti,VNodeq){intj=0;ArcNode*p;if(i==0)return0;else{while(j{p=G->adjlist[j].firstarc;if(p==NULL){j++;continue;}while(p->nextarc!=NULL&&p->adjvex!=i)p=p->nextarc;if(p->adjvex==i&&p->info+G->adjlist[j].ve>=G->adjlist[i].ve){G->adjlist[i].ve=p->info+G->adjlist[j].ve;j++;}elsej++;}returnG->adjlist[i].ve;}}/*获取事件发生最迟时间*/intget_vl(ALGraph*G,inti,VNodeq){ArcNode*p;p=G->adjlist[i].firstarc;if(i==0)return0;G->adjlist[i].vl=G->adjlist[p->adjvex].vl-p->info;//出度为0的顶点只有一个且已出栈,此处无需判断p!=NULLwhile(p->nextarc!=NULL){p=p->nextarc;if(G->adjlist[i].vl>=G->adjlist[p->adjvex].vl-p->info)G->adjlist[i].vl=G->adjlist[p->adjvex].vl-p->info;}returnG->adjlist[i].vl;}/*拓扑排序*/voidTopSort(ALGraph*G){inti,j;intSt[MAXV],top=-1;ArcNode*p;for(i=0;in;i++)/*入度置为0*/G->adjlist[i].count=0;for(i=0;in;i++)/*求所有顶点的入度*/{p=G->adjlist[i].firstarc;while(p!=NULL){G->adjlist[p->adjvex].count++;//终点入度+1p=p->nextarc;}}for(i=0;in;i++)/*入度为0的顶点进栈*/if(G->adjlist[i].count==0){top++;St[top]=i;}/*进行拓扑排序并将结果逐个输出*/while(top>-1)//栈不为空时进行循环{i=St[top];top--;printf("%c",G->adjlist[i]);p=G->adjlist[i].firstarc;while(p!=NULL){j=p->adjvex;G->adjlist[j].count--;if(G->adjlist[j].count==0){top++;St[top]=j;}p=p->nextarc;}}}/*关键路径及最短时间*/voidget_keyroad(ALGraph*G){inti=0,stime=0;ArcNode*p;printf("%c",G->adjlist[i]);while(in){p=G->adjlist[i].firstarc;while(p!=NULL){p->lt=G->adjlist[p->adjvex].vl-p->info;if(p->lt-G->adjlist[i].ve==0){p->flag=
{
G->adjlist[i].firstin=NULL;
G->adjlist[i].firstout=NULL;
G->adjlist[i].data=vex[i];
}
for(k=0;k{i=0;while(G->adjlist[i].data!=edge[k].vi)i++;j=0;while(G->adjlist[j].data!=edge[k].vj)j++;p=(ArcNode*)malloc(sizeof(ArcNode));p->headvex=i+1;//始边顶点(数组元素第一个序号为0)p->tailvex=j+1;//终边顶点p->info=edge[k].info;//权值p->hlink=G->adjlist[i].firstout;//建立弧头链表p->tlink=G->adjlist[j].firstin;//建立弧尾链表G->adjlist[i].firstout=G->adjlist[j].firstin=p;//头插}}/*查找指定顶点的位置(序号)*/intLocateVex(ALGraph*G,Vertexu){inti;for(i=0;i<(*G).n;i++)//循环查找结点if((*G).adjlist[i].data==u)returni;//如果找到该结点就返回其在十字链表中的序号return-1;//如果没有找到该结点,返回-1}/*求指定顶点的度*/intCount(ALGraph*G,Vertexu){inti=0,j=0,k;ArcNode*p,*q;k=LocateVex(G,u);//k是顶点v的序号if(k<0)//k不是图G的顶点return-1;p=(*G).adjlist[k].firstin;//找出弧q=(*G).adjlist[k].firstout;//找入弧while(p!=NULL&&p->tlink!=NULL)//依次扫描以该顶点为尾结点的弧{p=p->tlink;i++;}while(q!=NULL&&q->hlink==NULL)//依次扫描以该顶点为头结点的弧{q=q->hlink;j++;}returni+j;}/*插入新边*/boolInsertArc(ALGraph*G,etypea){inti,j;ArcNode*p;i=LocateVex(G,a.vi);//弧尾序号j=LocateVex(G,a.vj);//弧头序号if(i<0||j<0)returnfalse;p=(ArcNode*)malloc(sizeof(etype));//生成新结点p->tailvex=i;//给新结点赋值p->headvex=j;p->info=a.info;p->hlink=(*G).adjlist[j].firstin;//插在入弧和出弧的链头p->hlink=(*G).adjlist[i].firstout;(*G).adjlist[j].firstin=(*G).adjlist[i].firstout=p;(*G).e++;//弧数+1returntrue;}/*删除指定边*/boolDeletetArc(ALGraph*G,etypea){inti,j;ArcNode*p1,*p2;i=LocateVex(G,a.vi);//弧尾序号j=LocateVex(G,a.vj);//弧头序号if(i<0||j<0||i==j)returnfalse;p2=(*G).adjlist[i].firstout;//将弧结点从出弧表中删去if(p2&&p2->headvex==j)//第一个结点为待删除结点(*G).adjlist[i].firstout=p2->tlink;else{while(p2&&p2->headvex!=j){p1=p2;p2=p2->tlink;}if(p2)//没到表尾p1->tlink=p2->tlink;}p2=(*G).adjlist[j].firstin;//将弧结点从入弧链表中删去if(p2&&p2->tailvex==i)(*G).adjlist[j].firstin=p2->hlink;else{while(p2&&p2->tailvex!=i){p1=p2;p2=p2->hlink;}if(p2)//没到表尾p1->hlink=p2->hlink;}if(p2->info)//释放弧结点free(p2);(*G).e--;//弧数-1returntrue;}调试分析及问题解决1、typedefstructOLGArc,定义弧结点结构体,包含:两个整数类型的数据:tailvex、headve,q,分别为该弧的尾顶点、头顶点和该弧的权值;两个指针:*hlink、*tlink,分别为与该弧拥有相同头顶点的链域和与该弧拥有相同尾顶点的链域;2、typedefstructOLGVNode,定义顶点结点结构体,包含:一个字符类型的数据:data,为顶点的名字;两个指针:*firstin,*firstout,分别指向该顶点的第一条入弧和出弧;3、typedefstructALGraph,定义十字链表结构体,包含:一个OLGVNode类型的数组,用于存放表头向量;两个整数类型的数据:vexnum、arcnum,分别为有向图的当前顶点数和弧数;4、StatusCreateDG(ALGraph*G),创建十字链表的函数,实现创建空的十字链表,在十字链表中放入有向图的顶点、弧、权值等功能;5、voidDestroyGraph(ALGraph*G),销毁十字链表的函数,实现销毁用十字链表存储的有向图的功能;6、VertexType*GetVex(ALGraphG,intv),查找序号为v的顶点并返回该顶点的名字(即该顶点的值);7、建表时为了统一代码,由另一人更改了建表的操作,导致建表时从1开始计数而不是0,后面的查找和删除功能有误;更改后正常。8、在插入指定边时,插入边将原有的边覆盖;在查找指定结点的度时,获得的结果不正确,特别是在插入一条新边后的数据与原数据的差不为1。解决办法:通过调试查找,找到了错误来源:1、在创建十字链表时,数据的下标是从1开始存储的,而在“查找指定结点的度”、“插入边”、“删除边”等函数中均默认十字链表的存储下标是从0开始;2、在创建十字链表时,出现弧头和弧尾的定义与函数中的定义相反(如下图说明)。于是进行如下修改:1、将创建十字链表函数中的下标改为从0开始存储;2、将“查找指定结点的度”、“插入边”、“删除边”等函数出现的弧头相关信息改为弧尾的,将弧尾相关信息改为弧头的。再次经过调试,证明程序正确。关键路径功能概述输入有向图,求其关键路径及最短时间。模块简介创建有向图邻接表;creatlink(ALGraph*&G,charvex[],intn,etypeedge[],inte)输入有向图邻接表;Inputgraph(ALGraph*&G)获取事件发生最早时间;intget_ve(ALGraph*G,inti,VNodeq)获取时间发生最迟时间;intget_vl(ALGraph*G,inti,VNodeq)对有向图进行拓扑排序;TopSort(ALGraph*&G)求关键活动及最短时间。get_keyroad(ALGraph*G)数据类型/*边的数据类型*/typedefstruct{charvi,vj;intinfo;}etype;/*边结点数据类型*/typedefstructANode{intadjvex;/*邻接点存储序号*/intlt;/*活动最迟发生时间*/intflag;/*标记是否为关键活动*/InfoTypeinfo;/*若是网络存储权值*/structANode*nextarc;/*指向下一个边结点*/}ArcNode;/*表头结点数据类型*/typedefstruct{Vertexdata;/*存储顶点元素*/intcount;/*顶点入度*/intve,vl;/*事件最早及最迟发生时间*/ArcNode*firstarc;/*指向依附于该顶点的第一边*/}VNode;typedefVNodeAdjList[MAXV];/*邻接表数据类型*/typedefstruct{AdjListadjlist;intn,e;/*图中顶点数n和边数e*/}ALGraph;概要设计Creatlink(建表)此函数用于调试后面的功能传参:空表,顶点,顶点个数,边数,有向边信息邻接表为链式存储,传参使用的数组则为顺序存储。步骤为:1.将顶点信息(charvex[])放入表头结点(G->adjlist[i].data);2.将有向边信息(etypeedge[])填入表中作为边结点。为了方便调试采用尾插法,后期可能会改成头插。Inputgraph(输入图)此函数为正式建表传参:空的有向图步骤为:先输入顶点数和边数,确定邻接表大小后再进行边信息的录入。输入顶点信息,建立邻接表头->输入边信息,将边信息填入表中作为边结点。为了便于调试采用尾插法。TopSort(拓扑排序)传参:有向图(以邻接表方式存储)步骤为:利用栈存储入度为零的顶点,逐一输出,再删去其作为始点出发的边得出下一个入度为零的点,输出,重复上述步骤,即得拓扑排序序列。一边进行拓扑排序的同时一边调用get_ve(图,头结点序号,头结点)函数获得每个事件发生的最早时间,同时将序列存入一个栈St1以便后面获得拓扑排序逆序列。由于St1属于本函数中的局部变量,因此get_vl函数放在本函数里,对St1进行出栈操作,获取拓扑排序逆序列并求算事件最迟发生时间。get_keyroad(关键路径及最短时间)传参:有向图(以邻接表方式存储)遍历有向图,利用公式活动最迟发生时间=终点事件最迟发生时间-活动持续时间活动最早发生时间=起点事件最早发生时间求出关键活动,以此获得关键路径及最短时间。详细设计(源代码)/*建立邻接表*/voidcreatlink(ALGraph*&G,charvex[],intn,etypeedge[],inte){ArcNode*p,*t;t=(ArcNode*)malloc(sizeof(ArcNode));t->nextarc=NULL;G=(ALGraph*)malloc(sizeof(ALGraph));G->n=n;G->e=e;intk,i,j;for(i=0;i{G->adjlist[i].firstarc=NULL;G->adjlist[i].data=vex[i];G->adjlist[i].ve=0;}for(k=0;k{i=0;while(G->adjlist[i].data!=edge[k].vi)i++;j=0;while(G->adjlist[j].data!=edge[k].vj)j++;p=(ArcNode*)malloc(sizeof(ArcNode));p->adjvex=j;//终边顶点p->info=edge[k].info;//权值p->flag=0;//默认为非关键活动if(G->adjlist[i].firstarc==NULL)//尾插{G->adjlist[i].firstarc=p;p->nextarc=NULL;t=p;}else{t->nextarc=p;p->nextarc=NULL;t=p;}}}/*输入邻接表*/voidInputgraph(ALGraph*&G){chare[2];intl,i,j,k=0;//l:边长ArcNode*p,*t;t=(ArcNode*)malloc(sizeof(ArcNode));t->nextarc=NULL;G=(ALGraph*)malloc(sizeof(ALGraph));printf("pleaseinputvexandedgenumber:\n");//先输入顶点数和边数scanf("%d%d",&G->n,&G->e);for(i=0;in;i++)/*置入各表头结点(即顶点)*/{G->adjlist[i].data='a'+i;//自动生成顶点G->adjlist[i].firstarc=NULL;G->adjlist[i].ve=0;}while(ke)//输入边信息{printf("pleaseaddedgeinformation:\n");scanf("%s",e);scanf("%d",&l);i=0;while(G->adjlist[i].data!=e[0])i++;j=0;while(G->adjlist[j].data!=e[1])j++;p=(ArcNode*)malloc(sizeof(ArcNode));p->adjvex=j;//终边顶点p->info=l;//权值p->flag=0;//默认为非关键活动if(G->adjlist[i].firstarc==NULL)//尾插{G->adjlist[i].firstarc=p;p->nextarc=NULL;t=p;}else{t->nextarc=p;p->nextarc=NULL;t=p;}k++;}}/*获取事件发生最早时间*/intget_ve(ALGraph*G,inti,VNodeq){intj=0;ArcNode*p;if(i==0)return0;else{while(j{p=G->adjlist[j].firstarc;if(p==NULL){j++;continue;}while(p->nextarc!=NULL&&p->adjvex!=i)p=p->nextarc;if(p->adjvex==i&&p->info+G->adjlist[j].ve>=G->adjlist[i].ve){G->adjlist[i].ve=p->info+G->adjlist[j].ve;j++;}elsej++;}returnG->adjlist[i].ve;}}/*获取事件发生最迟时间*/intget_vl(ALGraph*G,inti,VNodeq){ArcNode*p;p=G->adjlist[i].firstarc;if(i==0)return0;G->adjlist[i].vl=G->adjlist[p->adjvex].vl-p->info;//出度为0的顶点只有一个且已出栈,此处无需判断p!=NULLwhile(p->nextarc!=NULL){p=p->nextarc;if(G->adjlist[i].vl>=G->adjlist[p->adjvex].vl-p->info)G->adjlist[i].vl=G->adjlist[p->adjvex].vl-p->info;}returnG->adjlist[i].vl;}/*拓扑排序*/voidTopSort(ALGraph*G){inti,j;intSt[MAXV],top=-1;ArcNode*p;for(i=0;in;i++)/*入度置为0*/G->adjlist[i].count=0;for(i=0;in;i++)/*求所有顶点的入度*/{p=G->adjlist[i].firstarc;while(p!=NULL){G->adjlist[p->adjvex].count++;//终点入度+1p=p->nextarc;}}for(i=0;in;i++)/*入度为0的顶点进栈*/if(G->adjlist[i].count==0){top++;St[top]=i;}/*进行拓扑排序并将结果逐个输出*/while(top>-1)//栈不为空时进行循环{i=St[top];top--;printf("%c",G->adjlist[i]);p=G->adjlist[i].firstarc;while(p!=NULL){j=p->adjvex;G->adjlist[j].count--;if(G->adjlist[j].count==0){top++;St[top]=j;}p=p->nextarc;}}}/*关键路径及最短时间*/voidget_keyroad(ALGraph*G){inti=0,stime=0;ArcNode*p;printf("%c",G->adjlist[i]);while(in){p=G->adjlist[i].firstarc;while(p!=NULL){p->lt=G->adjlist[p->adjvex].vl-p->info;if(p->lt-G->adjlist[i].ve==0){p->flag=
{i=0;
while(G->adjlist[i].data!
=edge[k].vi)
i++;
j=0;
while(G->adjlist[j].data!
=edge[k].vj)
j++;
p=(ArcNode*)malloc(sizeof(ArcNode));
p->headvex=i+1;//始边顶点(数组元素第一个序号为0)
p->tailvex=j+1;//终边顶点
p->info=edge[k].info;//权值
p->hlink=G->adjlist[i].firstout;//建立弧头链表
p->tlink=G->adjlist[j].firstin;//建立弧尾链表
G->adjlist[i].firstout=G->adjlist[j].firstin=p;//头插
/*查找指定顶点的位置(序号)*/
{inti;
for(i=0;i<(*G).n;i++)//循环查找结点
if((*G).adjlist[i].data==u)
returni;//如果找到该结点就返回其在十字链表中的序号
return-1;//如果没有找到该结点,返回-1
/*求指定顶点的度*/
{inti=0,j=0,k;
ArcNode*p,*q;
k=LocateVex(G,u);//k是顶点v的序号
if(k<0)//k不是图G的顶点
return-1;
p=(*G).adjlist[k].firstin;//找出弧
q=(*G).adjlist[k].firstout;//找入弧
while(p!
=NULL&&p->tlink!
=NULL)//依次扫描以该顶点为尾结点的弧
{p=p->tlink;
while(q!
=NULL&&q->hlink==NULL)//依次扫描以该顶点为头结点的弧
{q=q->hlink;
returni+j;
/*插入新边*/
{inti,j;
ArcNode*p;
i=LocateVex(G,a.vi);//弧尾序号
j=LocateVex(G,a.vj);//弧头序号
if(i<0||j<0)
returnfalse;
p=(ArcNode*)malloc(sizeof(etype));//生成新结点
p->tailvex=i;//给新结点赋值
p->headvex=j;
p->info=a.info;
p->hlink=(*G).adjlist[j].firstin;//插在入弧和出弧的链头
p->hlink=(*G).adjlist[i].firstout;
(*G).adjlist[j].firstin=(*G).adjlist[i].firstout=p;
(*G).e++;//弧数+1
returntrue;
/*删除指定边*/
ArcNode*p1,*p2;
if(i<0||j<0||i==j)
p2=(*G).adjlist[i].firstout;//将弧结点从出弧表中删去
if(p2&&p2->headvex==j)//第一个结点为待删除结点
(*G).adjlist[i].firstout=p2->tlink;
else
{while(p2&&p2->headvex!
=j)
{p1=p2;
p2=p2->tlink;
if(p2)//没到表尾
p1->tlink=p2->tlink;
p2=(*G).adjlist[j].firstin;//将弧结点从入弧链表中删去
if(p2&&p2->tailvex==i)
(*G).adjlist[j].firstin=p2->hlink;
{while(p2&&p2->tailvex!
=i)
p2=p2->hlink;
p1->hlink=p2->hlink;
if(p2->info)//释放弧结点
free(p2);
(*G).e--;//弧数-1
调试分析及问题解决
1、typedefstructOLGArc,定义弧结点结构体,包含:
两个整数类型的数据:
tailvex、headve,q,分别为该弧的尾顶点、头顶点和该弧的权值;两个指针:
*hlink、*tlink,分别为与该弧拥有相同头顶点的链域和与该弧拥有相同尾顶点的链域;
2、typedefstructOLGVNode,定义顶点结点结构体,包含:
一个字符类型的数据:
data,为顶点的名字;两个指针:
*firstin,*firstout,分别指向该顶点的第一条入弧和出弧;
3、typedefstructALGraph,定义十字链表结构体,包含:
一个OLGVNode类型的数组,用于存放表头向量;两个整数类型的数据:
vexnum、arcnum,分别为有向图的当前顶点数和弧数;
4、StatusCreateDG(ALGraph*G),创建十字链表的函数,实现创建空的十字链表,在十字链表中放入有向图的顶点、弧、权值等功能;
5、voidDestroyGraph(ALGraph*G),销毁十字链表的函数,实现销毁用十字链表存储的有向图的功能;
6、VertexType*GetVex(ALGraphG,intv),查找序号为v的顶点并返回该顶点的名字(即该顶点的值);
7、建表时为了统一代码,由另一人更改了建表的操作,导致建表时从1开始计数而不是0,后面的查找和删除功能有误;更改后正常。
8、在插入指定边时,插入边将原有的边覆盖;在查找指定结点的度时,获得的结果不正确,特别是在插入一条新边后的数据与原数据的差不为1。
解决办法:
通过调试查找,找到了错误来源:
1、在创建十字链表时,数据的下标是从1开始存储的,而在“查找指定结点的度”、“插入边”、“删除边”等函数中均默认十字链表的存储下标是从0开始;2、在创建十字链表时,出现弧头和弧尾的定义与函数中的定义相反(如下图说明)。
于是进行如下修改:
1、将创建十字链表函数中的下标改为从0开始存储;2、将“查找指定结点的度”、“插入边”、“删除边”等函数出现的弧头相关信息改为弧尾的,将弧尾相关信息改为弧头的。
再次经过调试,证明程序正确。
关键路径
输入有向图,求其关键路径及最短时间。
创建有向图邻接表;creatlink(ALGraph*&G,charvex[],intn,etypeedge[],inte)
输入有向图邻接表;Inputgraph(ALGraph*&G)
获取事件发生最早时间;intget_ve(ALGraph*G,inti,VNodeq)
获取时间发生最迟时间;intget_vl(ALGraph*G,inti,VNodeq)
对有向图进行拓扑排序;TopSort(ALGraph*&G)
求关键活动及最短时间。
get_keyroad(ALGraph*G)
/*边结点数据类型*/
typedefstructANode
{intadjvex;/*邻接点存储序号*/
intlt;/*活动最迟发生时间*/
intflag;/*标记是否为关键活动*/
InfoTypeinfo;/*若是网络存储权值*/
structANode*nextarc;/*指向下一个边结点*/
{Vertexdata;/*存储顶点元素*/
intcount;/*顶点入度*/
intve,vl;/*事件最早及最迟发生时间*/
ArcNode*firstarc;/*指向依附于该顶点的第一边*/
}VNode;
typedefVNodeAdjList[MAXV];
/*邻接表数据类型*/
Creatlink(建表)此函数用于调试后面的功能
空表,顶点,顶点个数,边数,有向边信息
邻接表为链式存储,传参使用的数组则为顺序存储。
步骤为:
1.将顶点信息(charvex[])放入表头结点(G->adjlist[i].data);2.将有向边信息(etypeedge[])填入表中作为边结点。
为了方便调试采用尾插法,后期可能会改成头插。
Inputgraph(输入图)此函数为正式建表
空的有向图
先输入顶点数和边数,确定邻接表大小后再进行边信息的录入。
输入顶点信息,建立邻接表头->输入边信息,将边信息填入表中作为边结点。
为了便于调试采用尾插法。
TopSort(拓扑排序)
有向图(以邻接表方式存储)
利用栈存储入度为零的顶点,逐一输出,再删去其作为始点出发的边得出下一个入度为零的点,输出,重复上述步骤,即得拓扑排序序列。
一边进行拓扑排序的同时一边调用get_ve(图,头结点序号,头结点)函数获得每个事件发生的最早时间,同时将序列存入一个栈St1以便后面获得拓扑排序逆序列。
由于St1属于本函数中的局部变量,因此get_vl函数放在本函数里,对St1进行出栈操作,获取拓扑排序逆序列并求算事件最迟发生时间。
get_keyroad(关键路径及最短时间)
遍历有向图,利用公式
活动最迟发生时间=终点事件最迟发生时间-活动持续时间
活动最早发生时间=起点事件最早发生时间
求出关键活动,以此获得关键路径及最短时间。
/*建立邻接表*/
voidcreatlink(ALGraph*&G,charvex[],intn,etypeedge[],inte)
{ArcNode*p,*t;
t=(ArcNode*)malloc(sizeof(ArcNode));
t->nextarc=NULL;
for(i=0;i{G->adjlist[i].firstarc=NULL;G->adjlist[i].data=vex[i];G->adjlist[i].ve=0;}for(k=0;k{i=0;while(G->adjlist[i].data!=edge[k].vi)i++;j=0;while(G->adjlist[j].data!=edge[k].vj)j++;p=(ArcNode*)malloc(sizeof(ArcNode));p->adjvex=j;//终边顶点p->info=edge[k].info;//权值p->flag=0;//默认为非关键活动if(G->adjlist[i].firstarc==NULL)//尾插{G->adjlist[i].firstarc=p;p->nextarc=NULL;t=p;}else{t->nextarc=p;p->nextarc=NULL;t=p;}}}/*输入邻接表*/voidInputgraph(ALGraph*&G){chare[2];intl,i,j,k=0;//l:边长ArcNode*p,*t;t=(ArcNode*)malloc(sizeof(ArcNode));t->nextarc=NULL;G=(ALGraph*)malloc(sizeof(ALGraph));printf("pleaseinputvexandedgenumber:\n");//先输入顶点数和边数scanf("%d%d",&G->n,&G->e);for(i=0;in;i++)/*置入各表头结点(即顶点)*/{G->adjlist[i].data='a'+i;//自动生成顶点G->adjlist[i].firstarc=NULL;G->adjlist[i].ve=0;}while(ke)//输入边信息{printf("pleaseaddedgeinformation:\n");scanf("%s",e);scanf("%d",&l);i=0;while(G->adjlist[i].data!=e[0])i++;j=0;while(G->adjlist[j].data!=e[1])j++;p=(ArcNode*)malloc(sizeof(ArcNode));p->adjvex=j;//终边顶点p->info=l;//权值p->flag=0;//默认为非关键活动if(G->adjlist[i].firstarc==NULL)//尾插{G->adjlist[i].firstarc=p;p->nextarc=NULL;t=p;}else{t->nextarc=p;p->nextarc=NULL;t=p;}k++;}}/*获取事件发生最早时间*/intget_ve(ALGraph*G,inti,VNodeq){intj=0;ArcNode*p;if(i==0)return0;else{while(j{p=G->adjlist[j].firstarc;if(p==NULL){j++;continue;}while(p->nextarc!=NULL&&p->adjvex!=i)p=p->nextarc;if(p->adjvex==i&&p->info+G->adjlist[j].ve>=G->adjlist[i].ve){G->adjlist[i].ve=p->info+G->adjlist[j].ve;j++;}elsej++;}returnG->adjlist[i].ve;}}/*获取事件发生最迟时间*/intget_vl(ALGraph*G,inti,VNodeq){ArcNode*p;p=G->adjlist[i].firstarc;if(i==0)return0;G->adjlist[i].vl=G->adjlist[p->adjvex].vl-p->info;//出度为0的顶点只有一个且已出栈,此处无需判断p!=NULLwhile(p->nextarc!=NULL){p=p->nextarc;if(G->adjlist[i].vl>=G->adjlist[p->adjvex].vl-p->info)G->adjlist[i].vl=G->adjlist[p->adjvex].vl-p->info;}returnG->adjlist[i].vl;}/*拓扑排序*/voidTopSort(ALGraph*G){inti,j;intSt[MAXV],top=-1;ArcNode*p;for(i=0;in;i++)/*入度置为0*/G->adjlist[i].count=0;for(i=0;in;i++)/*求所有顶点的入度*/{p=G->adjlist[i].firstarc;while(p!=NULL){G->adjlist[p->adjvex].count++;//终点入度+1p=p->nextarc;}}for(i=0;in;i++)/*入度为0的顶点进栈*/if(G->adjlist[i].count==0){top++;St[top]=i;}/*进行拓扑排序并将结果逐个输出*/while(top>-1)//栈不为空时进行循环{i=St[top];top--;printf("%c",G->adjlist[i]);p=G->adjlist[i].firstarc;while(p!=NULL){j=p->adjvex;G->adjlist[j].count--;if(G->adjlist[j].count==0){top++;St[top]=j;}p=p->nextarc;}}}/*关键路径及最短时间*/voidget_keyroad(ALGraph*G){inti=0,stime=0;ArcNode*p;printf("%c",G->adjlist[i]);while(in){p=G->adjlist[i].firstarc;while(p!=NULL){p->lt=G->adjlist[p->adjvex].vl-p->info;if(p->lt-G->adjlist[i].ve==0){p->flag=
G->adjlist[i].firstarc=NULL;
G->adjlist[i].ve=0;
for(k=0;k{i=0;while(G->adjlist[i].data!=edge[k].vi)i++;j=0;while(G->adjlist[j].data!=edge[k].vj)j++;p=(ArcNode*)malloc(sizeof(ArcNode));p->adjvex=j;//终边顶点p->info=edge[k].info;//权值p->flag=0;//默认为非关键活动if(G->adjlist[i].firstarc==NULL)//尾插{G->adjlist[i].firstarc=p;p->nextarc=NULL;t=p;}else{t->nextarc=p;p->nextarc=NULL;t=p;}}}/*输入邻接表*/voidInputgraph(ALGraph*&G){chare[2];intl,i,j,k=0;//l:边长ArcNode*p,*t;t=(ArcNode*)malloc(sizeof(ArcNode));t->nextarc=NULL;G=(ALGraph*)malloc(sizeof(ALGraph));printf("pleaseinputvexandedgenumber:\n");//先输入顶点数和边数scanf("%d%d",&G->n,&G->e);for(i=0;in;i++)/*置入各表头结点(即顶点)*/{G->adjlist[i].data='a'+i;//自动生成顶点G->adjlist[i].firstarc=NULL;G->adjlist[i].ve=0;}while(ke)//输入边信息{printf("pleaseaddedgeinformation:\n");scanf("%s",e);scanf("%d",&l);i=0;while(G->adjlist[i].data!=e[0])i++;j=0;while(G->adjlist[j].data!=e[1])j++;p=(ArcNode*)malloc(sizeof(ArcNode));p->adjvex=j;//终边顶点p->info=l;//权值p->flag=0;//默认为非关键活动if(G->adjlist[i].firstarc==NULL)//尾插{G->adjlist[i].firstarc=p;p->nextarc=NULL;t=p;}else{t->nextarc=p;p->nextarc=NULL;t=p;}k++;}}/*获取事件发生最早时间*/intget_ve(ALGraph*G,inti,VNodeq){intj=0;ArcNode*p;if(i==0)return0;else{while(j{p=G->adjlist[j].firstarc;if(p==NULL){j++;continue;}while(p->nextarc!=NULL&&p->adjvex!=i)p=p->nextarc;if(p->adjvex==i&&p->info+G->adjlist[j].ve>=G->adjlist[i].ve){G->adjlist[i].ve=p->info+G->adjlist[j].ve;j++;}elsej++;}returnG->adjlist[i].ve;}}/*获取事件发生最迟时间*/intget_vl(ALGraph*G,inti,VNodeq){ArcNode*p;p=G->adjlist[i].firstarc;if(i==0)return0;G->adjlist[i].vl=G->adjlist[p->adjvex].vl-p->info;//出度为0的顶点只有一个且已出栈,此处无需判断p!=NULLwhile(p->nextarc!=NULL){p=p->nextarc;if(G->adjlist[i].vl>=G->adjlist[p->adjvex].vl-p->info)G->adjlist[i].vl=G->adjlist[p->adjvex].vl-p->info;}returnG->adjlist[i].vl;}/*拓扑排序*/voidTopSort(ALGraph*G){inti,j;intSt[MAXV],top=-1;ArcNode*p;for(i=0;in;i++)/*入度置为0*/G->adjlist[i].count=0;for(i=0;in;i++)/*求所有顶点的入度*/{p=G->adjlist[i].firstarc;while(p!=NULL){G->adjlist[p->adjvex].count++;//终点入度+1p=p->nextarc;}}for(i=0;in;i++)/*入度为0的顶点进栈*/if(G->adjlist[i].count==0){top++;St[top]=i;}/*进行拓扑排序并将结果逐个输出*/while(top>-1)//栈不为空时进行循环{i=St[top];top--;printf("%c",G->adjlist[i]);p=G->adjlist[i].firstarc;while(p!=NULL){j=p->adjvex;G->adjlist[j].count--;if(G->adjlist[j].count==0){top++;St[top]=j;}p=p->nextarc;}}}/*关键路径及最短时间*/voidget_keyroad(ALGraph*G){inti=0,stime=0;ArcNode*p;printf("%c",G->adjlist[i]);while(in){p=G->adjlist[i].firstarc;while(p!=NULL){p->lt=G->adjlist[p->adjvex].vl-p->info;if(p->lt-G->adjlist[i].ve==0){p->flag=
p->adjvex=j;//终边顶点
p->flag=0;//默认为非关键活动
if(G->adjlist[i].firstarc==NULL)//尾插
{G->adjlist[i].firstarc=p;
p->nextarc=NULL;
t=p;
{t->nextarc=p;
/*输入邻接表*/
voidInputgraph(ALGraph*&G)
{chare[2];
intl,i,j,k=0;//l:
边长
ArcNode*p,*t;
printf("pleaseinputvexandedgenumber:
\n");//先输入顶点数和边数
scanf("%d%d",&G->n,&G->e);
for(i=0;in;i++)/*置入各表头结点(即顶点)*/
G->adjlist[i].data='a'+i;//自动生成顶点
while(ke)//输入边信息
{printf("pleaseaddedgeinformation:
\n");
scanf("%s",e);
scanf("%d",&l);
i=0;
=e[0])
=e[1])
p->info=l;//权值
k++;
/*获取事件发生最早时间*/
intget_ve(ALGraph*G,inti,VNodeq)
{intj=0;
if(i==0)return0;
{while(j
{p=G->adjlist[j].firstarc;
if(p==NULL)
{j++;
continue;
while(p->nextarc!
=NULL&&p->adjvex!
p=p->nextarc;
if(p->adjvex==i&&
p->info+G->adjlist[j].ve>=G->adjlist[i].ve)
{G->adjlist[i].ve=p->info+G->adjlist[j].ve;
elsej++;
returnG->adjlist[i].ve;
/*获取事件发生最迟时间*/
intget_vl(ALGraph*G,inti,VNodeq)
p=G->adjlist[i].firstarc;
G->adjlist[i].vl=G->adjlist[p->adjvex].vl-p->info;//出度为0的顶点只有一个且已出栈,此处无需判断p!
=NULL
=NULL)
{p=p->nextarc;
if(G->adjlist[i].vl>=G->adjlist[p->adjvex].vl-p->info)
G->adjlist[i].vl=G->adjlist[p->adjvex].vl-p->info;
returnG->adjlist[i].vl;
/*拓扑排序*/
voidTopSort(ALGraph*G)
intSt[MAXV],top=-1;
for(i=0;in;i++)/*入度置为0*/
G->adjlist[i].count=0;
for(i=0;in;i++)/*求所有顶点的入度*/
{p=G->adjlist[i].firstarc;
{G->adjlist[p->adjvex].count++;//终点入度+1
for(i=0;in;i++)/*入度为0的顶点进栈*/
if(G->adjlist[i].count==0)
{top++;
St[top]=i;
/*进行拓扑排序并将结果逐个输出*/
while(top>-1)//栈不为空时进行循环
{i=St[top];top--;
printf("%c",G->adjlist[i]);
{j=p->adjvex;
G->adjlist[j].count--;
if(G->adjlist[j].count==0)
St[top]=j;
/*关键路径及最短时间*/
voidget_keyroad(ALGraph*G)
{inti=0,stime=0;
while(in)
{p->lt=G->adjlist[p->adjvex].vl-p->info;
if(p->lt-G->adjlist[i].ve==0)
{p->flag=
copyright@ 2008-2022 冰豆网网站版权所有
经营许可证编号:鄂ICP备2022015515号-1