拓扑排序课程设计.docx
《拓扑排序课程设计.docx》由会员分享,可在线阅读,更多相关《拓扑排序课程设计.docx(17页珍藏版)》请在冰豆网上搜索。
![拓扑排序课程设计.docx](https://file1.bdocx.com/fileroot1/2022-11/27/974bcfcc-e7f1-49b6-b4fa-b8de59af376b/974bcfcc-e7f1-49b6-b4fa-b8de59af376b1.gif)
拓扑排序课程设计
长沙理工大学
《数据结构》课程设计报告
拓扑排序算法的研究与实现
赵思雨
学院计算机与通信工程专业网络工程
班级网络1101班学号2011
学生姓名赵思雨指导教师乐晓波
课程成绩完成日期2013年7月12日
课程设计任务书
计算机与通信工程学院网络工程专业
课程名称
数据结构课程设计
时间
2012-2013学年第2学期
19周--20周
学生姓名
赵思雨
指导老师
乐晓波
题目
拓扑排序算法的研究与实现
主要内容:
研究图的存储结构,研究AOV网(活动在顶点的网,有向网)的存储结构与输入算法,并研究拓扑排序算法的实现方法,在此基础上对该算法进行分析。
要求:
(1)研究AOV网(活动在顶点的网,有向网)的存储结构与输入算法,并研究拓扑排序算法的实现方法。
(2)通过对拓扑排序问题的分析、设计、编码、测试等工作,掌握针对实际应用问题设计数据结构,结合C语言解决实际应用问题的一般方法和过程,初步掌握利用数据结构解决实际应用问题的一般方法。
(3)对所设计的算法要求进行认真的分析、测试与调试,所提交的相关程序要能正确运行。
(4)按要求认真撰写课程设计报告书。
应当提交的文件:
(1)课程设计报告书打印稿一份。
(2)课程设计相关电子文档一套(含任务书、报告书、可正确执行的程序等)。
课程设计成绩评定
学院计算机与通信工程专业网络工程
班级网络11-01学号2011
学生姓名赵思雨指导教师乐晓波
完成日期2013年7月12日
指导教师对学生在课程设计中的评价
评分项目
优
良
中
及格
不及格
课程设计中的创造性成果
学生掌握课程内容的程度
课程设计完成情况
课程设计动手能力
文字表达
学习态度
规范要求
课程设计论文的质量
指导教师对课程设计的评定意见
综合成绩指导教师签字年月日
拓扑排序算法的研究与实现
学生姓名:
赵思雨指导老师:
乐晓波
摘要该课程设计研究AOV网。
研究图的存储结构,研究AOV网(活动在顶点的网,有向网)的存储结构与输入算法,并研究拓扑排序算法的实现方法,在此基础上对该算法进行分析。
通过对拓扑排序问题的分析、设计、编码、测试等工作,掌握针对实际应用问题设计数据结构,结合C语言解决实际应用问题的一般方法和过程,初步掌握利用数据结构解决实际应用问题的一般方法。
关键字AOV网;拓扑排序;算法设计;C语言;数据结构
1引言
课程设计是培养学生综合运用所学知识,发现,提出,分析和解决实际问题,锻炼实践能力的重要环节,是对学生实际工作能力的具体训练和考察过程。
数据结构是学习计算机相关专业的非常重要的知识,所谓结构就是组织形式,数据的结构就是数据怎么组织,即怎么描述,怎么在电脑中存储。
不同类型的数据,它们的组织形式(数据结构)是不同的,在程序设计中,除了应精心设计算法外,还应精心组织数据(包括原始数据、中间结果、最终结果),使之形成一定的组织形式(数据结构),以便让计算机尽可能高效率地处理。
《数据结构》是计算机科学与工程的基础研究之一,掌握该领域的知识对于我们进一步进行高效率的计算机程序开发非常重要。
无论在中国还是在美国,《数据结构》一直是大学的计算机专业重要的专业基础课。
数据结构的课程设计要求学生熟练掌握数据结构的逻辑特性和物理表示,具有分析问题的能力,可以根据问题选择合适的数据结构,运用该数据结构结合相应的算法解决实际问题。
1.1课程设计的目的
为了更好的学习数据结构,深刻理解数据结构在解决实际问题中的应用,体会其重要性,熟练掌握线性表、栈和队列、串、数组、树、图等常用的数据结构,熟悉各自的特点和应用场合。
同时锻炼自己独立分析理解问题的能力,学会根据不同的问题选择合适的数据结构,然后结合适当的算法解决问题。
锻炼自己的设计和编写程序的技巧,进一步调试和测试自己所写的程序,使其功能更加完善,养成较好的编写程序习惯。
提高综合运用所学的理论知识和方法独立分析和解决问题的能力[1],训练用系统的观点和软件开发一般规范进行软件开发,培养软件工作者所应具备的科学的工作方法和作风。
本课程设计的目的就是要达到理论与实际应用相结合,使同学们能够根据数据对象的特性,学会数据组织的方法,能把现实世界中的实际问题在计算机内部表示出来,并培养基本的、良好的程序设计技能[2]。
1.2课程设计的内容
本次课程设计要求对于给定的AOV网求出它所有拓扑序列。
AOV网(是一个有向无环图(DirectedAcyclineGraph,DAG图)[3]。
AOV网中,如果顶点Vi表示的活动在和顶点Vj表示的活动之前进行,则称Vi是Vj的前驱顶点,Vj是Vi后继顶点。
拓扑排序就是将有向无环图中的各个顶点排成一个序列,使得所有的前去后继关系都得到满足。
对于相互之间没有次序关系的顶点,在拓扑排序的序列中可以处在任意的位置。
因此,拓扑排序的结果往往是不唯一的。
本次课程设计的主要任务就是将给定的一个AOV网输出所有的该种序列[4]。
1.3课程设计的目标
通过对拓扑排序问题的分析、设计、编码、测试等工作,掌握针对实际应用问题设计数据结构,结合C语言解决实际应用问题的一般方法和过程,初步掌握利用数据结构解决实际应用问题的一般方法。
2设计内容
2.1问题描述
在AOV网中为了更好地完成工程,必须满足活动之间先后关系,需要将各活动排一个先后次序即为拓扑排序。
拓扑排序可以应用于教学计划的安排,根据课程之间的依赖关系,制定课程安排计划。
按照用户输入的课程数,课程间的先后关系数目以及课程间两两间的先后关系,程序执行后会给出符合拓扑排序的课程安排计划。
2.2思路分析
一种拓扑序列的生成一般有一下步骤:
(1)、从有向无环图中选择一个没有前驱结点的顶点并加入到结点的序列中;
(2)、从有向无环图中删除该顶点以及该顶点为尾的所有的弧;
(3)、重复
(1)
(2)的步骤。
在整个拓扑排序的过程中需要频繁的检查顶点的前驱以及作删除顶点和弧的操作,在这里我们用两个全局变量rudu[max_vertex_num]和visited[max_vertex_num]来分别实现这两个操作,如果rudu[i]为零则表示无前驱结点,如果visited[i]为零则表示没有被访问过。
如果每删除一个结点就检查,那样的话时间复杂度将很大(等于遍历所有的顶点一遍),因此设计一个堆栈,把检测到的入度为零的结点入栈。
每次删除顶点只要从栈中取出一个结点即可。
但是如果需要实现一个AOV网所有拓扑序列的生成则不止如此。
在每找到一个符合要求的结点后入栈,从而实现一种拓扑排序。
在一趟拓扑排序结束后,应该进行恢复删除的结点和删除的弧,并标记已经有过的序列,在恢复某几个个结点后进行下一次的拓扑排序。
2.3过程演示
该部分给出了算法执行的流程,如图2-1所示。
图2-1算法流程图
3算法分析及详细实现
3.1算法分析
首先建立空栈,并从第一个顶点开始进行拓扑排序。
将该结点初始化为被访问过,并将图类的结点指针指向该编号的结点的表头数组firstarc域,把该顶点入栈后,将所有的以它为起点的弧都删掉,即将弧的终点的入度都减一。
接着判断栈里面的数据个数是否和图顶点的个数一样,如果一样,则说明已经有一次拓扑排序完成,若不一样,则往下进行递归,再将符合条件的顶点入栈,直到一次拓扑排序完成的条件成立。
最后将顶点出栈,恢复所有结点的入度,并准备下一趟拓扑排序。
3.2算法中用到的函数声明
在该程序中用到了很多函数,具体声明如表3-1所示。
print(ALGraph*T)
输出图的邻接表
Sinsert(Stack*L,intx)
入栈
Sdelete(Stack*L)
出栈
Soutput(Stack*L)
输出栈中元素
topusort(Stack*L,ALGraph*G,int
拓扑排序
表3-1算法中用到的函数声明
3.3部分程序编写
实现该算法的具体编码如下:
voidtopusort(Stack*L,ALGraph*G,inti){//拓扑排序
ListNode*P;
intj,k;Soutput(L);
Sinsert(L,i);//把顶点Vi加入到栈中
P=G->head[i].firstarc;
visited[i]=1;//把排序过的点标记
while(P){
j=P->number;
rudu[j]--;//把以Vi为头的终止结点入度减1
P=P->next;
}
if(L->top+1==G->vexnum){//判断栈中一种拓扑排序完成
Soutput(L);
printf("\n");
flag++;
}
for(k=0;kvexnum;k++)//调用部分
if((visited[k]==0)&&(rudu[k]==0))//如果Vk在此轮中未被访问过且入度为0
topusort(L,G,k);
visited[i]=0;//使Vi恢复为未访问
P=G->head[i].firstarc;
while(P)
{
j=P->number;
rudu[j]++;//使Vj的入度恢复,加1
P=P->next;
}
Sdelete(L);
}
4程序的运行环境及运行结果
4.1程序运行的环境
一个程序需要一个良好的环境才能运行,该课程设计中的程序运行的硬件、软件环境如下。
(1)硬件环境
1)学生用微机
2)局域网
(2)软件环境
1)Windows8旗舰版系统
2)VS2010
4.2运行结果
程序的运行结果将从三个方面予以介绍。
(1)图的输入
输入图,结果如图4-1所示。
在这里首先输入顶点数和边数,然后输入没条边的起点终点编号和权值。
图4-1图的输入
(2)图邻接表的输出
输出图的邻接矩阵。
输入图过后按回车键显示图的邻接表,如图4-2所示。
图4-2图邻接表的输出
(3)拓扑序列的输出
在显示完拓扑序列后按回车输出该图的所有拓扑排序序列,如图4-3所示。
图4-3拓扑序列的输出
5总结
5.1课程设计总结
课程设计可以检验学生对知识的掌握程度。
同时更反映了一个学生对课程设计的态度,对待任何事都要认真,可以不会,可以做的不是最好的,但是一定要尽自己最大的努力去完成一件哪怕再小的事。
在这次课程设计中学到了很多新的知识,同时也巩固了之前学过知识。
对静态链表有了更深的理解,对哈夫曼树及哈夫曼编码更是印象深刻。
虽然,最后根据要求完成了任务,但期间还是出现了种种的问题,比如在编写完函数时,运行结果总是不对,调试了好多遍,也没有发现错误,没有想到是选择函数除了差错,一个简单的函数,怎么也没有想到会是这里出了错,是因为我给s1,s2变量赋初值时出了错误。
同时,为了更好的完成任务还查阅了好多资料,不懂的就到处搜索,去图书馆借相应的书籍来看,锻炼了自己查阅资料的能力。
5.2心得与体会
我觉得本此课程设计最大的收获就是学会按部就班的完成任务。
首先想好用什么数据结构,主要的思想要明确的认定。
然后选一个简单的例子,按照你选定的思想,一步一步的走程序,才能更早的发现程序还存在那些没有考虑到的缺陷。
特别忌讳的是到写程序的时候,对主要思想和数据结构还不是很确定。
最后在调试程序的阶段,这次我开始用设置断点的方法,一步一步看是否达到你要的理想结果,从而来找出程序中出错的地方。
还有,一种方法也是用一个输出语句,分别放在程序的不同位置,通过输出结果,也可以快速判断出程序不能运行的是那一个语句了。
从而方便你修改,快速得到正确的结果。
还有,这次调试的过程中,我表现的比以前要更有耐心,这也是一个很好的收获。
还有一个更重要的体会就是,遇到不会的地方,我开始习惯到图书馆或是网上去找资料。
这是一个很好的学习过程,它不仅仅是可以解决这个问题,关键是你会从找资料学习的过程中,会学好很多很多意想不到的知识。
可能会比你手头上要学习的知识多得多。
参考文献
[1]严蔚敏,吴伟民.数据结构(C语言版)[M].北京:
清华大学出版社,2012.5
[2]李春葆,尹为民.数据结构教程上机指导[M].北京:
清华大学出版社,2008
[3]AndrewBinstock,JohnRex.程序员实用算法(第二版)[M].北京:
机械工业出版社,2009.06
[4]ThomasH.Cormen,CharlesE.Leiserson,RonaldL.Riverst,ClffordStein.IntroductiontoAlogrithmsSecondEdition[M].US:
MachinePress,2012
附件
程序清单
#include"stdio.h"
#include"malloc.h"
#include"stdlib.h"
#definemax_vertex_num100//顶点的最大个数
typedefstructnode{//邻接表中的结点类型
intnumber;//结点编号
structnode*next;//指向下一个结点
intinfo;//与表头结点边的信息
}ListNode;
typedefstruct{//定义表头结点数组
intnumber;//顶点信息
ListNode*firstarc;//指向第一个邻接点
}AdjList;//表头结点类型
typedefstruct{//邻接表类型
AdjListhead[max_vertex_num];//邻接表的组
intvexnum;//顶点个数表头数
intedgnum;//边的个数
}ALGraph;
typedefstruct{
intdata[max_vertex_num];//堆栈数组
inttop;//栈顶标志
}Stack;//顺序表类型
intrudu[max_vertex_num];//定义一个存储每个顶点入度的全局数组
intvisited[max_vertex_num];//定义全局数组标志拓扑排序过程中是否访问过
intflag=0;//接受拓扑排序的次数,若为0,则该图不是AOV网
ALGraph*create_AdjListGraph(){
ALGraph*T;ListNode*p;//定义指向结点的指针
inti,j;//输入边是的起点和终点
T=(ALGraph*)malloc(sizeof(ALGraph));//定义图
printf("1、请输入顶点数:
");
scanf("%d",&T->vexnum);//输入顶点个数
for(i=0;ivexnum;i++){//初始化表头结点数组
T->head[i].number=i;//初始化结点编号
rudu[i]=0;//将每个节点的荼毒初始化为
visited[i]=0;
T->head[i].firstarc=NULL;//将表头结点数组的每个结点的指针域置空
}
printf("2、请输入边数:
");
scanf("%d",&T->edgnum);//输入边的个数
printf("3、按起点终点权值的顺序一次输入边的序列:
\n");
for(i=0;iedgnum;i++){
printf("请输入第%d条边:
",i+1);
p=(ListNode*)malloc(sizeof(ListNode));
scanf("%d",&j);//输入这条边起始节点的编号
scanf("%d",&p->number);//输入这条边的终点的编号
scanf("%d",&p->info);//输入这条边的权值
printf("\n");
rudu[p->number]++;//将插入边中结点的入度加1
p->next=T->head[j].firstarc;
T->head[j].firstarc=p;//将该结点插入到单链表中创建一条边
}
returnT;
}
voidprint(ALGraph*T){//输出图的邻接表
ListNode*p;
inti;
printf("表头数组(该结点的入度)与之有边的节点(该弧的权值)\n");
for(i=0;ivexnum;i++){
printf("V%d(%d)",T->head[i].number,rudu[i]);
p=T->head[i].firstarc;
while(p){
printf("---------------->V%d(%d)",p->number,p->info);
p=p->next;
}
printf("\n");
}
}
Stack*Setnull(){//置栈空
Stack*L;
L=(Stack*)malloc(sizeof(Stack));
L->top=-1;
return(L);
}
voidSinsert(Stack*L,intx){//入栈
L->top=L->top+1;
L->data[L->top]=x;
}
voidSdelete(Stack*L){//出栈
L->top=L->top-1;
}
voidSoutput(Stack*L){//输出栈中元素
inti;
for(i=0;itop+1;i++)
printf("V%d",L->data[i]);
printf("\n");
}
voidtopusort(Stack*L,ALGraph*G,inti){//拓扑排序
ListNode*P;
intj,k;Soutput(L);
Sinsert(L,i);//把顶点Vi加入到顺序表中
P=G->head[i].firstarc;
visited[i]=1;//把排序过的点标记
while(P){
j=P->number;
rudu[j]--;//把以Vi为头的终止结点入度减1
P=P->next;
}
if(L->top+1==G->vexnum){//判断顺序表中一种拓扑排序完成
Soutput(L);
printf("\n");
flag++;
}
for(k=0;kvexnum;k++)//调用部分
if((visited[k]==0)&&(rudu[k]==0))//如果Vk在此轮中未被访问过且入度为0
topusort(L,G,k);
visited[i]=0;//使Vi恢复为未访问
P=G->head[i].firstarc;
while(P)
{
j=P->number;
rudu[j]++;//使Vj的入度恢复,加1
P=P->next;
}
Sdelete(L);
}
voidcaidan(){
printf("\t_____________________________________________________________\n\n");
printf("\t◇◇\n");
printf("\t◇◇\n");
printf("\t◇**********欢迎使用AOV网拓扑序列显示程序!
**********◇\n");
printf("\t◇◇\n");
printf("\t◇◇\n");
printf("\t_____________________________________________________________\n");
}
voidcaidan1(){
printf("\n\n\n\n\n");
printf("\t_____________________________________________________________\n\n");
printf("\t◇◇\n");
printf("\t◇◇\n");
printf("\t◇★★★★谢谢使用!
★★★★◇\n");
printf("\t◇◇\n");
printf("\t◇◇\n");
printf("\t_____________________________________________________________\n");
}
voidmain()
{
inti;ALGraph*G;Stack*L;intn=1;
L=Setnull();
while(n==1){//循环显示拓扑排序
system("cls");
caidan();
printf("注意:
请按下列步骤输入您的AOV网!
\n");
G=create_AdjListGraph();
system("cls");
caidan();
printf("该图的邻接表为:
\n");
print(G);
system("pause");
system("cls");
caidan();
printf("以下是该图所有的拓扑序列:
\n");
for(i=0;ivexnum;i++)
if(rudu[i]==0&&visited[i]==0)topusort(L,G,i);
if(flag>0){
printf("该AOV图有%d种拓扑排序.\n",flag);
flag=0;
}
else
printf("\n\n\t出错!
\n\n此图不是AOV网,无拓扑排序序列.\n\n\n");
system("pause");
system("cls");
caidan();
printf("按1继续显示下一个图的拓扑序列,按0结束程序!
\n");
scanf("%d",&n);
system("cls");
if(n==0){caidan1();}
}
}