1、 数据结构课程设计 设计说明书 有向图拓扑排序算法实现 学生姓名 樊佳佳 学号 班级 网络工程1301 成绩 指导老师 申静 数学和计算机科学学院 1月4日 课程设计任务书 —第一学期 课程设计名称: 数据结构课程设计 课程设计题目: 图拓扑排序算法实现 完 成 期 限: 自 12月20日至 1
2、月 3 日共 2 周 设计内容: 1、设计任务 (1)给出一个有向无环图,遍历全部节点;(2)能够实现对全部顶点拓扑;(3)界面友好,可操作性强。 2、需求分析 对系统功效及性能要求进行分析,写出需求规格说明书(可行性分析汇报、系统分层DFD图)。 3、软件设计 软件设计分两个阶段进行:总体设计和具体设计; 总体设计:确定系统总体设计方案,完成系统模块结构图及模块功效说明; 具体设计:对模块内部过程及数据结构进行设计,和进行数据库设计、用户界面设计等编写出该项目标具体设计汇报; 4、具体编码 编写程序,要求给出具体注释,包含:模块名、模块功效、中间过程功效、 变量说明等。
3、同时编写用户使用手册、程序模块说明等文档; 5、软件测试 全部测试过程要求采取综合测试策略:先作静态分析,再作动态测试。应事先制订测试计划,并要求保留全部测试用例,完成测试汇报。 指导老师:申静 教研室责任人:申静 课程设计评阅 评语: 指导老师署名: 年 月 日 摘 要 设计了一个对有向图进行拓扑排序算法,该算
4、法首先用邻接表结构有向图存放结构,然后对此有向图进行拓扑排序,输出拓扑排序结果。用VC++作为软件开发环境,以邻接表作为图存放结构,将图中全部顶点排成一个线性序列,输出拓扑排序结果。拓扑排序常见来确定一个依靠关系集中,事物发生次序。拓扑排序是对有向无环图顶点一个排序,它使得假如存在一条从顶点A到顶点B路径,那么在排序中B出现在A后面。 关键词:邻接表;有向无环图;拓扑排序 目 录 1 课题描述 1 2 问题分析和任务定义 2 3 逻辑设计 3 3.1程序模块功效图 3 3.2 抽象数据类型 3 4 具体设计 4 4.1 C语言定义相关数据类型 4 4.2 关键模
5、块伪码算法 4 4.2.1主函数伪码算法 4 4.2.2邻接表伪码算法 4 4.2.3拓扑排序伪码算法: 5 4.3 主函数步骤图 6 5 程序编码 7 6 程序调试和测试 13 7 结果分析 16 8 总结 17 参考文件 18 1 课题描述 根依据设计要求利用C语言程序设计了一个对有向图进行拓扑排序算法,该算法首先用邻接表结构有向图存放结构,然后对此有向图进行拓扑排序,输出拓扑排序结果。 如给定一个有向无环图图1.1所表示。在此图中,从入度为0顶点出发,删除此顶点和全部以它为尾弧;反复直至全部顶点均已输出;或当图中不存在无前驱顶点为止。 2
6、 3 1 4 5 图1.1 有向无环图 开发工具:Visual C++ 6.0 2 问题分析和任务定义 对一个有向无环图G进行拓扑排序,是将G中全部顶点排成一个线性序列,使得图中任意一对由某个集合上一个偏序得到该集合上一个全序,这个操作就称之为拓扑排序。偏序集合中仅有部分组员之间颗比较,而全序指集合中全体组员之间均可比较,而由偏序定义得到拓扑有序操作便是拓扑排序。 一个表示偏序有向图可用来表示一个步骤
7、图,经过抽象出来就是AOV-网,若从顶点i到顶点j有一条有向路径,则i是j前驱,j是i后继。若(i,j)是一条弧,则i是j直接前驱;j是i直接后继。 在AOV-网中,不应该出现有向环,用拓扑排序就能够判定网中是否有环,若网中全部顶点全部在它拓扑有序序列中,则该AOV-网肯定不存在环。 3 逻辑设计 主函数 3.1程序模块功效图 拓扑排序 节点入度 栈初始化 创建邻接表 有向图初始化 图3.1程序模块功效图 3.2 抽象数据类型 ADT ALGraph { 数据对象:D={V|V是含有相同特征数据元素集合,即顶点
8、集}
数据关系:R={
9、计 4.1 C语言定义相关数据类型 #define max_vextex_num 20 /*宏定义最大顶点个数*/ #define stack_init_size 100 /*宏定义栈存放空间大小*/ typedef int ElemType; typedef struct VNode /*邻接表头结点类型*/ { int data; /*顶点信息,数据域*/ }VNode, AdjList[MAX_VEXTEX_NUM]; /*AdjList是邻接表类型*/ typedef struct { AdjList vertices;
10、 /*邻接表*/ int vexnum, arcnum; /*图中顶点数vexn和边数arcn*/ }ALGraph; /*图类型*/ typedef struct //构建栈 { ElemType *base; /*数据域*/ ElemType *top; /*栈指针域*/ int stacksize; }SqStack; 4.2 关键模块伪码算法 4.2.1主函数伪码算法: 开始 { 创建及输出邻接表CreatGraphlist(&G); 输出排序后输出序列TopologicalSort(G); } 结束 4.2.2
11、邻接表伪码算法: #define MAX_VEXTEX_NUM 20 typedef struct VNode /*邻接表头结点类型*/ { int data; /*顶点信息,数据域*/ ArcNode *firstarc; /*指向第一条弧*/ }VNode, AdjList[MAX_VEXTEX_NUM]; /*AdjList是邻接表类型*/ typedef struct { AdjList vertices; /*邻接表*/ int vexnum, arcnum; /*图中顶点数vexn和边数arcn*/ }ALGraph; /*图类型*/
12、 开始 { 定义一个指针P 置i初值为1 邻接表中全部头结点指针置初值 当i<=G-vexnum时自加,实施下面操作: 输出数据域里顶点信息 使指针p指向顶点i第一条弧头结点 输出访问顶点 使指针p指向顶点i下一条弧头结点 类此循环到输出最终一个顶点 } 结束 4.2.3拓扑排序伪码算法 开始 { 引入栈操作函数和入度操作函数 访问邻接存放表中顶点n If该顶点入度为0 顶点进栈 循环操作到全部顶点入栈 当栈不为空 顶点出栈 } 结束 4.3 主函数步骤图 主函数步骤图图4.3所表示 开始
13、
输入顶点数和弧数
N
输入
判定是否满足条件
Y
依据输入信息建立邻接表
建栈
在邻接表中寻求入度为零顶点,使其入栈
N
输出栈顶元素,打印,将和栈顶元素邻接顶点入度减1
判定是否空栈
Y
结束
图4.3 主函数程序步骤图
5 程序编码
#include
14、TEX_NUM 20 #define M 20 #define STACK_INIT_SIZE 100 #define STACKINCREMENT 10 /*-----------------------图邻接表存放结构------------------------*/ typedef struct ArcNode /*弧结点结构类型*/ { int adjvex; /*该弧指向顶点位置*/ struct ArcNode *nextarc;
15、 /*指向下一条弧指针*/ }ArcNode; typedef struct VNode /*邻接表头结点类型*/ { int data; /*顶点信息*/ ArcNode *firstarc; /*指向第一条依附于该点弧指针*/ }VNode,AdjList[MAX_VEXTEX_NUM]; /*AdjList为邻接表类型*/ typedef
16、struct { AdjList vertices; int vexnum, arcnum; }ALGraph; /*----------------------------------------------------------------*/ void CreatGraph(ALGraph *G) /*经过用户交互产生一个图邻接表*/ { int m, n, i; ArcNode *p; printf("=======================================================
17、"); 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; /*编写顶点位置
18、序号*/ 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("输入顶点序号不正确 请重新输入:");
19、 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].firsta
20、rc; /*下一条弧指向是依附于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-
21、>vertices[i].firstarc;p;p=p->nextarc) printf("-->%d",p->adjvex); printf("\n"); } printf("======================================================="); } /*----------------------------------------------------------------*/ typedef struct
22、 /*栈存放结构*/ { int *base; /*栈底指针*/ int *top; /*栈顶指针*/ int stacksize; }SqStack; /*----------------------------------------------------------------*/ void InitStack(SqStack *S) /*初始
23、化栈*/ { 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; } /*----------------------------------------------------------------*/
24、 void Push(SqStack *S,int e) /*压入新元素为栈顶*/ { if(S->top-S->base>=S->stacksize) { S->base=(int *)realloc(S->base,(S->stacksize+STACKINCREMENT)*sizeof(int)); /*追加新空间*/ if(!S->base) /*存放分配失败*/ {
25、printf("ERROR!"); exit(1); } S->top=S->base+S->stacksize; S->stacksize+=STACKINCREMENT; } *S->top++=e; /*e作为新栈顶元素*/ } /*----------------------------------------------------------------*/ int Pop(SqStack *S,int *e)
26、 /*弹出栈顶,用e返回*/ { if(S->top==S->base) /*栈为空*/ { return false; } *e=*--S->top; return 0; } /*----------------------------------------------------------------*/ int StackEmpty(SqStack *S) /*判定栈是否为空,为空返回1,不为空返回0*/ { if(S->top==S->base) r
27、eturn true; else return false; } /*----------------------------------------------------------------*/ void FindInDegree(ALGraph G, int indegree[]) /*对各顶点求入度*/ { int i; for(i=1; i<=G.vexnum;i++) /*入度赋初值0*/ { indegree[i]=0; } for(i
28、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; } } }
29、 /*----------------------------------------------------------------*/ void TopoSort(ALGraph G) { int indegree[M]; int i, k, n; int count=0; /*初始化输出计数器*/ ArcNode *p; SqStack S; FindInDegree(G,indegree); InitStack(&S); for(i=1;i
30、<=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("=======================================
31、"); printf("\n\n拓扑排序序列为:"); while(!StackEmpty(&S)) /*栈不为空*/ { Pop(&S,&n); /*弹出栈顶*/ printf("%4d",G.vertices[n].data); /*输出栈顶并计数*/ count++; for(p=G.vertic
32、es[n].firstarc; p!=NULL;p=p->nextarc)
/*n号顶点每个邻接点入度减一*/
{
k=p->adjvex;
if(!(--indegree[k])) /*若入度减为零,则再入栈*/
{
Push(&S,k);
}
}
}
if(count 33、
}
else
{
printf(" 排序成功!");
}
}
/*----------------------------------------------------------------*/
main(void) /*编写主调函数以调用上述被调函数*/
{
ALGraph G;
CreatGraph(&G); /*建立邻接表*/
TopoSort(G); 34、 /*对图G进行拓扑排序*/
printf("\n\n");
system("pause");
/*调用系统dos命令:pause;显示:"按任意键继续..."*/
return 0;
}
6 程序调试和测试
(1)当为有向有环图结构图6.3所表示
图6.3 有向有环图结构
输出结果图6.4所表示
图6.4有向有环图输出
(2)输入检验图图6.5所表示:
图6.5 检验图
由邻接表定义能够得到上图邻接表图6.6所表示:
图6.6邻接表
35、
其中一个拓扑序列: 2 7 1 3 4 6 5
将图输入到程序中结果图6.7所表示:
图6.8 检验图输出
所得结果和估计结果一致。
7 结果分析
对于算法时间复杂度和空间复杂度, 拓扑排序实际是对邻接表表示图G进行遍历过程,每次访问一个入度为零顶点,若图G中没有回路,则需扫描邻接表中全部边结点,在算法开始时,为建立入度数组D需访问表头向量中全部边结点,算法时间复杂度为O(n+e)。
其次在编写代码时应依据步骤图进行同时编写,综合考虑需求分析进行编辑。不然会出现偏离专题错误。
当然在输出结果后,为避免输入引发错误,因该先对开始和结束结果进行先得出,和 36、运行结果对照,小问题应该进行尽可能避免,或减小到最小值。
8 总结
平时我就比较爱好编程,有时候自己也会设想部分小程序,然后经过自己努力来实现。所以我把此次课程设计看成了又一次锻炼,拿到题目后,经过和组员讨论便开始了程序编写。
大家全部知道,编程是一件很需要耐心事。因为几乎每一个程序编写,全部需要学习新知识才能进行,同时程序调试过程很枯燥,有时候一点小错意味着长时间查错。如语法错误中,“;”丢失、“{”和“}”不匹配等问题最难定位到犯错语句;逻辑错误中,作为循环变量“i”和“j”相互混淆、对未分配空间节点进行操作等,全部会使程序运行犯错而难以找到原因。算法设计、程序调试过程中,常常碰 37、到看似简单但又无法处理问题,这时候很轻易气馁丧气,此时切不可烦躁,一定要冷静思索,认真分析;而处理问题,完成程序后,那种兴奋感和成就感也令人振奋。能够说编写程序既是一件艰苦工作,又是一件愉快事情。
我小组课程设计题目标关键是“拓扑排序”。即使平时对拓扑排序有部分了解,上课也学过,但真正应用到程序中,写出算法却一点也不简单。拓扑排序,首先需要有被排序主体,也就是有向图,于是先要实现有向图建立及相关操作;有向图建立,该选择怎样数据结构,是邻接矩阵还是邻接表,本着尽可能靠近实际应用态度,我选择了节省存放空间邻接表;拓扑排序要将图中零入度顶点先输出,可利用栈或队列实现,而本程序一个应用是实现教学计划 38、安排,考虑到教学计划安排实际情况,通常先学各门基础课(入度为零),再学专业课(入度不为零),和队列优异先出特点相符,故采取队列实现。总而言之,什么地方该用什么数据结构,该写出怎样算法,全部要经过精心分析和仔细考虑。
课程设计不是一个人任务,而是一个小组三个组员共同任务,不仅要能完成程序,而且在完成过程中也要让团体有效地协作起来。在此次课程设计过程中,我认识到以下几点:第一,要有奉献精神,不要怕自己多做了,不要怕自己负担任务有多重,而其它组员做极少。多去做一点不会吃亏,还能学到更多东西。团体组员之间应团结互助,不计功过得失;第二,分工上不能马虎,要具体到个人,每个人负责哪部分任务,什么时候完成 39、全部要有明确说明,应各尽其能,做到资源优化配置;第三,具体工作时,各组员应频繁交流,避免各自为政,最终造成函数功效不符要求,参数调用不方便,或是论文作者无从下手;第四,当工作出现问题时,各组员应仔细商讨,立即找到问题症结,决不应推卸责任,更不能相互埋怨。
在完成课程设计过程中,我加深了对程序结构了解程度,对多种语句了解也更透彻,学会了灵活利用。同时体会到了团体合作乐趣,一向惯于“独立思索”我们学会了主动地同团体组员交流,取长补短,共同进步,只有和同学多交流多学习才能不停地提升本身水平。
总而言之,这学期数据结构课程设计,让我们学到了很多,受益匪浅。
参考文件
[1] 严蔚敏,吴伟民.数据结构(C语言版)[M]. 北京:清华大学出版社,
[2] 李春葆.数据结构(C语言版)习题和解析[M]. 北京:清华大学出版社,
[3] 钱能.C++程序设计教程[M]. 北京:清华大学出版社,
[4] 谭浩强.C程序设计.第三版[M]. 北京.清华大学出版社,.
[5] 张晓莉,刘振鹏,许百成.数据结构和算法[M]. 北京:机械工业出版社,
[6] 叶核亚.数据结构(C++版)[M].北京:机械工业出版社,






