数据结构课程设计有向图拓扑排序算法的实现.docx
《数据结构课程设计有向图拓扑排序算法的实现.docx》由会员分享,可在线阅读,更多相关《数据结构课程设计有向图拓扑排序算法的实现.docx(19页珍藏版)》请在冰豆网上搜索。
![数据结构课程设计有向图拓扑排序算法的实现.docx](https://file1.bdocx.com/fileroot1/2022-11/24/5b49fb31-87e1-4da0-857d-92cd5237601b/5b49fb31-87e1-4da0-857d-92cd5237601b1.gif)
数据结构课程设计有向图拓扑排序算法的实现
数据结构课程设计
设计说明书
有向图拓扑排序算法的实现
学生姓名
樊佳佳
学号
1318064017
班级
网络工程1301
成绩
指导教师
申静
数学与计算机科学学院
2016年1月4日
课程设计任务书
2015—2016学年第一学期
课程设计名称:
数据结构课程设计
课程设计题目:
图的拓扑排序算法的实现
完成期限:
自2015年12月20日至2016年1月3日共2周
设计内容:
1、设计任务
(1)给出一个有向无环图,遍历所有的节点;
(2)能够实现对所有顶点的拓扑;(3)界面友好,可操作性强。
2、需求分析
对系统的功能及性能要求进行分析,写出需求规格说明书(可行性分析报告、系统的分层DFD图)。
3、软件设计
软件设计分两个阶段进行:
总体设计和详细设计;
总体设计:
确定系统总体设计方案,完成系统的模块结构图及模块的功能说明;
详细设计:
对模块内部过程及数据结构进行设计,以及进行数据库设计、用户界面设计等编写出该项目的详细设计报告;
4、具体编码
编写程序,要求给出详细的注释,包括:
模块名、模块功能、中间过程的功能、变量说明等。
同时编写用户使用手册、程序模块说明等文档;
5、软件测试
所有测试过程要求采用综合测试策略:
先作静态分析,再作动态测试。
应事先制订测试计划,并要求保留所有测试用例,完成测试报告。
指导教师:
申静教研室负责人:
申静
课程设计评阅
评语:
指导教师签名:
年月日
摘要
设计了一个对有向图进行拓扑排序的算法,该算法首先用邻接表构造有向图的存储结构,然后对此有向图进行拓扑排序,输出拓扑排序的结果。
用VC++作为软件开发环境,以邻接表作为图的存储结构,将图中所有顶点排成一个线性序列,输出拓扑排序结果。
拓扑排序常用来确定一个依赖关系集中,事物发生的顺序。
拓扑排序是对有向无环图的顶点的一种排序,它使得如果存在一条从顶点A到顶点B的路径,那么在排序中B出现在A的后面。
关键词:
邻接表;有向无环图;拓扑排序
1课题描述
根根据设计要求运用C语言程序设计了一个对有向图进行拓扑排序的算法,该算法首先用邻接表构造有向图的存储结构,然后对此有向图进行拓扑排序,输出拓扑排序的结果。
如给定一个有向无环图如图1.1所示。
在此图中,从入度为0的顶点出发,删除此顶点和所有以它为尾的弧;重复直至全部顶点均已输出;或者当图中不存在无前驱的顶点为止。
图1.1有向无环图
开发工具:
VisualC++6.0
2问题分析和任务定义
对一个有向无环图G进行拓扑排序,是将G中所有顶点排成一个线性序列,使得图中任意一对由某个集合上的一个偏序得到该集合上的一个全序,这个操作就称之为拓扑排序。
偏序集合中仅有部分成员之间颗比较,而全序指集合中全体成员之间均可比较,而由偏序定义得到拓扑有序的操作便是拓扑排序。
一个表示偏序的有向图可用来表示一个流程图,通过抽象出来就是AOV-网,若从顶点i到顶点j有一条有向路径,则i是j的前驱,j是i的后继。
若(i,j)是一条弧,则i是j的直接前驱;j是i的直接后继。
在AOV-网中,不应该出现有向环,用拓扑排序就可以判断网中是否有环,若网中所有顶点都在它的拓扑有序序列中,则该AOV-网必定不存在环。
3逻辑设计
3.1程序模块功能图
图3.1程序模块功能图
3.2抽象数据类型
ADTALGraph
{
数据对象:
D={V|V是具有相同特性的数据元素的集合,即顶点集}
数据关系:
R={|v,w∈V,表示顶点v到顶点w的弧}
基本操作P:
CreatGraphlist(ALGraph*G)
初始条件:
成对输入顶点集V中的点。
操作结果:
构造图G的邻接表。
FindInDegree(ALGraphG,intindegree[])
初始条件:
图G的邻接表中存在结点V。
操作结果:
找到图中入度为0结点。
Initgraph()
操作结果:
完成图形初始化。
TopologicalSort(ALGraphG)
初始条件:
构造的有向图G已初始化。
操作结果:
对于有向图G根据邻接存储表进行拓扑排序。
}
4详细设计
4.1C语言定义的相关数据类型
#definemax_vextex_num20/*宏定义最大顶点个数*/
#definestack_init_size100/*宏定义栈的存储空间大小*/
typedefintElemType;
typedefstructVNode/*邻接表头结点的类型*/
{
intdata;/*顶点信息,数据域*/
}VNode,AdjList[MAX_VEXTEX_NUM];/*AdjList是邻接表类型*/
typedefstruct
{
AdjListvertices;/*邻接表*/
intvexnum,arcnum;/*图中顶点数vexn和边数arcn*/
}ALGraph;/*图的类型*/
typedefstruct//构建栈
{
ElemType*base;/*数据域*/
ElemType*top;/*栈指针域*/
intstacksize;
}SqStack;
4.2主要模块的伪码算法
4.2.1主函数伪码算法:
开始
{
创建及输出邻接表CreatGraphlist(&G);
输出排序后的输出序列TopologicalSort(G);
}
结束
4.2.2邻接表伪码算法:
#defineMAX_VEXTEX_NUM20
typedefstructVNode/*邻接表头结点的类型*/
{
intdata;/*顶点信息,数据域*/
ArcNode*firstarc;/*指向第一条弧*/
}VNode,AdjList[MAX_VEXTEX_NUM];/*AdjList是邻接表类型*/
typedefstruct
{
AdjListvertices;/*邻接表*/
intvexnum,arcnum;/*图中顶点数vexn和边数arcn*/
}ALGraph;/*图的类型*/
开始
{
定义一个指针P
置i的初值为1
邻接表中所有头结点指针置初值
当i<=G-vexnum时自加,执行下面操作:
输出数据域里的顶点信息
使指针p指向顶点i第一条弧的头结点
输出访问顶点
使指针p指向顶点i的下一条弧的头结点
类此循环到输出最后一个顶点
}
结束
4.2.3拓扑排序的伪码算法
开始
{
引入栈操作函数和入度操作函数
访问邻接存储表中的顶点n
If该顶点入度为0
顶点进栈
循环操作到所有顶点入栈
当栈不为空
顶点出栈
}
结束
4.3主函数流程图
主函数流程图如图4.3所示
N
输入
Y
N
Y
图4.3主函数程序流程图
5程序编码
#include
#include
#definetrue1
#definefalse0
#defineMAX_VEXTEX_NUM20
#defineM20
#defineSTACK_INIT_SIZE100
#defineSTACKINCREMENT10
/*-----------------------图的邻接表存储结构------------------------*/
typedefstructArcNode/*弧结点结构类型*/
{
intadjvex;/*该弧指向的顶点的位置*/
structArcNode*nextarc;/*指向下一条弧的指针*/
}ArcNode;
typedefstructVNode/*邻接表头结点类型*/
{
intdata;/*顶点信息*/
ArcNode*firstarc;/*指向第一条依附于该点的弧的指针*/
}VNode,AdjList[MAX_VEXTEX_NUM];/*AdjList为邻接表类型*/
typedefstruct
{
AdjListvertices;
intvexnum,arcnum;
}ALGraph;
/*----------------------------------------------------------------*/
voidCreatGraph(ALGraph*G)/*通过用户交互产生一个图的邻接表*/
{
intm,n,i;
ArcNode*p;
printf("=======================================================");
printf("\n输入顶点数:
");
scanf("%d",&G->vexnum);
printf("\n输入边数:
");
scanf("%d",&G->arcnum);
printf("=======================================================");
for(i=1;i<=G->vexnum;i++)/*初始化各顶点*/
{
G->vertices[i].data=i;/*编写顶点的位置序号*/
G->vertices[i].firstarc=NULL;
}
for(i=1;i<=G->arcnum;i++)/*记录图中由两点确定的弧*/
{
printf("\n输入确定弧的两个顶点u,v:
");
scanf("%d%d",&n,&m);
while(n<0||n>G->vexnum||m<0||m>G->vexnum)
{
printf("输入的顶点序号不正确请重新输入:
");
scanf("%d%d",&n,&m);
}
p=(ArcNode*)malloc(sizeof(ArcNode));/*开辟新的弧结点来存储用户输入的弧信息*/
if(p==NULL)
{
printf("ERROR!
");
exit
(1);
}
p->adjvex=m;/*该弧指向位置编号为m的结点*/
p->nextarc=G->vertices[n].firstarc;
/*下一条弧指向的是依附于n的第一条弧*/
G->vertices[n].firstarc=p;
}
printf("=======================================================");
printf("\n建立的邻接表为:
\n");
/*打印生成的邻接表(以一定的格式)*/
for(i=1;i<=G->vexnum;i++)
{
printf("%d",G->vertices[i].data);
for(p=G->vertices[i].firstarc;p;p=p->nextarc)
printf("-->%d",p->adjvex);
printf("\n");
}
printf("=======================================================");
}
/*----------------------------------------------------------------*/
typedefstruct/*栈的存储结构*/
{
int*base;/*栈底指针*/
int*top;/*栈顶指针*/
intstacksize;
}SqStack;
/*----------------------------------------------------------------*/
voidInitStack(SqStack*S)/*初始化栈*/
{
S->base=(int*)malloc(STACK_INIT_SIZE*sizeof(int));
if(!
S->base)/*存储分配失败*/
{
printf("ERROR!
");
exit
(1);
}
S->top=S->base;
S->stacksize=STACK_INIT_SIZE;
}
/*----------------------------------------------------------------*/
voidPush(SqStack*S,inte)/*压入新的元素为栈顶*/
{
if(S->top-S->base>=S->stacksize)
{
S->base=(int*)realloc(S->base,(S->stacksize+STACKINCREMENT)*sizeof(int));/*追加新空间*/
if(!
S->base)/*存储分配失败*/
{
printf("ERROR!
");
exit
(1);
}
S->top=S->base+S->stacksize;
S->stacksize+=STACKINCREMENT;
}
*S->top++=e;/*e作为新的栈顶元素*/
}
/*----------------------------------------------------------------*/
intPop(SqStack*S,int*e)/*弹出栈顶,用e返回*/
{
if(S->top==S->base)/*栈为空*/
{
returnfalse;
}
*e=*--S->top;
return0;
}
/*----------------------------------------------------------------*/
intStackEmpty(SqStack*S)/*判断栈是否为空,为空返回1,不为空返回0*/
{
if(S->top==S->base)
returntrue;
else
returnfalse;
}
/*----------------------------------------------------------------*/
voidFindInDegree(ALGraphG,intindegree[])/*对各顶点求入度*/
{
inti;
for(i=1;i<=G.vexnum;i++)/*入度赋初值0*/
{
indegree[i]=0;
}
for(i=1;i<=G.vexnum;i++)
{
while(G.vertices[i].firstarc)
{
indegree[G.vertices[i].firstarc->adjvex]++;
/*出度不为零,则该顶点firstarc域指向的弧指向的顶点入度加一*/
G.vertices[i].firstarc=G.vertices[i].firstarc->nextarc;
}
}
}
/*----------------------------------------------------------------*/
voidTopoSort(ALGraphG)
{
intindegree[M];
inti,k,n;
intcount=0;/*初始化输出计数器*/
ArcNode*p;
SqStackS;
FindInDegree(G,indegree);
InitStack(&S);
for(i=1;i<=G.vexnum;i++)
{
printf("\n");
printf("indegree[%d]=%d\n",i,indegree[i]);/*输出入度*/
}
printf("\n");
for(i=1;i<=G.vexnum;i++)/*入度为0的入栈*/
{
if(!
indegree[i])
Push(&S,i);
}
printf("=======================================================");
printf("\n\n拓扑排序序列为:
");
while(!
StackEmpty(&S))/*栈不为空*/
{
Pop(&S,&n);/*弹出栈顶*/
printf("%4d",G.vertices[n].data);/*输出栈顶并计数*/
count++;
for(p=G.vertices[n].firstarc;p!
=NULL;p=p->nextarc)
/*n号顶点的每个邻接点入度减一*/
{
k=p->adjvex;
if(!
(--indegree[k]))/*若入度减为零,则再入栈*/
{
Push(&S,k);
}
}
}
if(count{
printf("ERROR出现错误!
");
}
else
{
printf("排序成功!
");
}
}
/*----------------------------------------------------------------*/
main(void)/*编写主调函数以调用上述被调函数*/
{
ALGraphG;
CreatGraph(&G);/*建立邻接表*/
TopoSort(G);/*对图G进行拓扑排序*/
printf("\n\n");
system("pause");
/*调用系统的dos命令:
pause;显示:
"按任意键继续..."*/
return0;
}
6程序调试与测试
(1)当为有向有环图结构如图6.3所示
图6.3有向有环图结构
输出结果如图6.4所示
图6.4有向有环图的输出
(2)输入检验图如图6.5所示:
图6.5检验图
由邻接表定义可以得到上图的邻接表如图6.6所示:
图6.6邻接表
其中一种拓扑序列:
2713465
将图输入到程序中结果如图6.7所示:
图6.8检验图的输出
所得结果与预计结果一致。
7结果分析
对于算法的时间复杂度和空间复杂度,拓扑排序实际是对邻接表表示的图G进行遍历的过程,每次访问一个入度为零的顶点,若图G中没有回路,则需扫描邻接表中的所有边结点,在算法开始时,为建立入度数组D需访问表头向量中的所有边结点,算法的时间复杂度为O(n+e)。
其次在编写代码时应根据流程图进行同步编写,综合考虑需求分析进行编辑。
否则会出现偏离主题的错误。
当然在输出结果后,为避免输入引起的错误,因该先对开始与结束结果进行先得出,与运行结果对照,小的问题应该进行尽量的避免,或减小到最小值。
8总结
平时我就比较爱好编程,有时候自己也会设想一些小程序,然后通过自己的努力来实现。
因此我把本次课程设计当作了又一次锻炼,拿到题目后,经过与组员的讨论便开始了程序的编写。
大家都知道,编程是一件很需要耐心的事。
因为几乎每一个程序的编写,都需要学习新的知识才能进行,同时程序调试过程很枯燥,有时候一点小错意味着长时间的查错。
如语法错误中,“;”丢失、“{”与“}”不匹配等问题最难定位到出错语句;逻辑错误中,作为循环变量的“i”与“j”相互混淆、对未分配空间的节点进行操作等,都会使程序运行出错而难以找到原因。
算法设计、程序调试的过程中,经常遇到看似简单但又无法解决的问题,这时候很容易灰心丧气,此时切不可烦躁,一定要冷静的思考,认真的分析;而解决问题,完成程序后,那种兴奋感与成就感也令人振奋。
可以说编写程序既是一件艰苦的工作,又是一件愉快的事情。
我的小组课程设计题目的核心是“拓扑排序”。
虽然平时对拓扑排序有一些了解,上课也学过,但真正应用到程序中,写出算法却一点也不简单。
拓扑排序,首先需要有被排序的主体,也就是有向图,于是先要实现有向图的建立及相关操作;有向图的建立,该选取怎样的数据结构,是邻接矩阵还是邻接表,本着尽量靠近实际应用的态度,我选择了节省存储空间的邻接表;拓扑排序要将图中零入度顶点先输出,可利用栈或队列实现,而本程序的一个应用是实现教学计划的安排,考虑到教学计划安排的实际情况,一般先学各门基础课(入度为零),再学专业课(入度不为零),与队列先进先出的特点相符,故采用队列实现。
总之,什么地方该用什么数据结构,该写出怎样的算法,都要经过精心分析和仔细考虑。
课程设计不是一个人的任务,而是一个小组三个成员共同的任务,不但要能完成程序,而且在完成的过程中也要让团队有效地协作起来。
在本次课程设计的过程中,我认识到以下几点:
第一,要有奉献精神,不要怕自己多做了,不要怕自己承担的任务有多重,而其他成员做的很少。
多去做一点不会吃亏,还能学到更多的东西。
团队成员之间应团结互助,不计功过得失;第二,分工上不能马虎,要具体到个人,每个人负责哪部分任务,什么时候完成,都要有明确的说明,应各尽其能,做到资源优化配置;第三,具体工作时,各成员应频繁交流,避免各自为政,最后导致函数功能不符要求,参数调用不方便,或是论文作者无从下手;第四,当工作出现问题时,各成员应仔细商讨,尽快找到问题的症结,决不应推卸责任,更不能相互埋怨。
在完成课程设计的过程中,我加深了对程序结构的了解程度,对各种语句理解也更透彻,学会了灵活运用。
同时体会到了团队合作的乐趣,一向惯于“独立思考”的我们学会了积极地同团队成员交流,取长补短,共同进步,只有和同学多交流多学习才能不断地提高自身水平。
总之,这学期的数据结构课程设计,让我们学到了很多,受益匪浅。
参考文献
[1]严蔚敏,吴伟民.数据结构(C语言版)[M].北京:
清华大学出版社,2002
[2]李春葆.数据结构(C语言版)习题与解析[M].北京:
清华大学出版社,2002
[3]钱能.C++程序设计教程[M].北京:
清华大学出版社,2003
[4]谭浩强.C程序设计.第三版[M].北京.清华大学出版社,2005.
[5]张晓莉,刘振鹏,许百成.数据结构与算法[M].北京:
机械工业出版社,2002
[6]叶核亚.数据结构(C++版)[M].北京:
机械工业出版社,2004