AOV网的拓扑序列生成数据结构与算法课程设计报告Word下载.docx
《AOV网的拓扑序列生成数据结构与算法课程设计报告Word下载.docx》由会员分享,可在线阅读,更多相关《AOV网的拓扑序列生成数据结构与算法课程设计报告Word下载.docx(16页珍藏版)》请在冰豆网上搜索。
![AOV网的拓扑序列生成数据结构与算法课程设计报告Word下载.docx](https://file1.bdocx.com/fileroot1/2022-12/13/0825158b-09c1-4879-8f15-bab5cd42b690/0825158b-09c1-4879-8f15-bab5cd42b6901.gif)
typedefstruct{//邻接表类型
AdjListhead[max_vertex_num];
//邻接表的组
intvexnum,edgnum;
//顶点个数、边的个数
}ALGraph;
-1-
一种拓扑序列的生成一般有一下步骤:
(1)、从有向无环图中选择一个没有前驱结点的顶点并加入到结点的序列中;
(2)、从有向无环图中删除该顶点以及该顶点为尾的所有的弧;
(3)、重复
(1)
(2)的步骤。
在整个拓扑排序的过程中需要频繁的检查顶点的前驱以及作删除顶点和弧的操作,在这
里我们用两个全局变量rudu[max_vertex_num]和visited[max_vertex_num]来分别实现这
两个操作,如果rudu[i]为零则表示无前驱结点,如果visited[i]为零则表示没有被访问过。
如果每删除一个结点就检查,那样的话时间复杂度将很大(等于遍历所有的顶点一遍),因
此设计一个堆栈,把检测到的入度为零的结点入栈。
每次删除顶点只要从栈中取出一个结点即可。
这里堆栈也有一个数据结构,具体实现如图三所示:
-2-
typedefstruct{
intdata[max_vertex_num];
//堆栈数组
inttop;
//栈顶标志
}Stack;
//顺序表类型
但是如果需要实现一个AOV网所有拓扑序列的生成则不止如此。
在每找到一个符合要求
的结点后入栈,从而实现一种拓扑排序。
在一趟拓扑排序结束后,应该进行恢复删除的结点
和删除的弧,并标记已经有过的序列,在恢复某几个个结点后进行下一次的拓扑排序。
这里
用到递归的思想。
具体实现见下面的详细设计。
三、详细设计和编码
本次课程设计的重点在于输出AOV网的所有拓扑序列,因此图的创建即输出之类的问
题不做为重点,在此设计过程略过。
对于拓扑排序算法流程图如图四所示:
-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){//判断栈中一种拓扑排序完成
printf("
\n"
);
flag++;
for(k=0;
k<
G->
vexnum;
k++)//调用部分
if((visited[k]==0)&
&
(rudu[k]==0))//如果Vk在此轮中未被访问过且入度为0
topusort(L,G,k);
visited[i]=0;
//使Vi恢复为未访问
while(P)
{
rudu[j]++;
//使Vj的入度恢复,加1
Sdelete(L);
首先建立空栈,并从第一个顶点开始进行拓扑排序。
将该结点初始化为被访问过,并将
图类的结点指针指向该编号的结点的表头数组firstarc域,把该顶点入栈后,将所有的以它为起点的弧都删掉,即将弧的终点的入度都减一。
接着判断栈里面的数据个数是否和图顶点的个数一样,如果一样,则说明已经有一次拓扑排序完成,若不一样,则往下进行递归,
再将符合条件的顶点入栈,直到一次拓扑排序完成的条件成立。
最后将顶点出栈,恢复所有结点的入度,并准备下一趟拓扑排序。
注意,在这里,可能再某一次拓扑排序完成后,所有
的结点还没有来得及全部出栈,则发现了又可以入栈的情况如图一中的第二、四种排序就属
于这种例子。
四、上机调试
这里我按照做课程设计的过程,将在其中遇到的问题和解决的办法都写在这里。
1、课程设计开始,确定使用什么样的数据结构来存储图的时候,在邻接表和邻接矩阵之间进行徘徊。
课本上使用的事邻接表,我当时想自己重新想,不按照课本上已设计好的来,但是在随后的操作中,发现在对于每个结点进行查找,删除等操作的时候才发现,用邻接矩阵很不方便,也正是在此才明白用邻接表的优越性。
-4-
2、接下来就是创建图及输出图的一些函数的编写。
这部分比较简单,除了一些语法错误外没有很严重的错误。
这归功于以前在每次做实验的时候,这些基本的算法实现都有,直接按照这里的数据结构复制过来就行了。
3、下面是本次课程设计的核心部分。
(1)、在拓扑排序里面有必须设计一个数据结构来接受从图里面扫描出来没有入度且没有被访问过的结点。
开始的时候对于该数据结构的选择很是犯难,这让
开始一天,程序毫无进展,想过用队列用栈用顺序表,但是思想都很混乱,在将以前写过的这些基础函数都用到这里还是不能解决问题。
结果我想,这个数据结构到底有什么样的特点,要什么样的功能。
基于该数据结构必须实现要将开始所有的符合条件的顶点保存起来,在判断保存的数据个数是否等于图的顶点个数后,才将所有的数据的输出,作为一次拓扑排序结束。
在此判断符合栈的先进先出的特点,最终确定使用栈的数据结构。
其实在这里对于栈到底是使用顺序栈还是链栈也是一个需要考虑的问题,一般链栈要比顺序栈要灵活,但是这里最终要用栈里面的元素个数判断是否一次拓扑排序结束,而顺序栈里面的栈顶元素正好有这个功能,所有这里选择用顺序栈。
(2)、接下来就是递归的具体实现了,开始的时候设计了一个计算入度的函数,但是后来发现这样做不好。
第一、每次在删除顶点后都要计算入度,这样算法的性能里面时间和空间的复杂度都要大大的增加;
第二,每次在计算入度的时候都要将图中所有的顶点都加进去,而在某些阶段,有许多的顶点已经相当于被删掉了,这样,失去了备份,连整个图都被破坏了程序会不稳定,健壮性得不到肯定。
所以在这里使用了一个全局数组,用于存储每个顶点的入度,而不作为图的一个部分。
这样在每次删除恢复顶点的时候都很简单。
(3)、在这里遇到语法问题和很简单,大多使一些,参数没写,逗号没写等问题。
仔细编译即便都得到解决了,但是下面的一些逻辑问题则用了我很多的时间去解决。
a、在删除以某个顶点为起点的弧时,没有用另外一个变量去代替形参i,导致
出现混乱,有的顶点并没有作为第一个扫描对象而使许多排序情况没有。
b、在一次拓扑排序结束后,要恢复顶点的入度。
在此开始没有想到要将该顶点的访问设为未访问,而至使只能输出一次拓扑排序。
c、在恢复某个单链表中所有的结点的入度的时候,要将该类型的指针先指向该表头数组的指针firstarc域.否则这部分的循环根本就没有运行。
因为在前面指针P在删除弧的时候已经指空了。
d、在一般的情况都实现了过后有一个很严重的问题,就是在输入的图不是A
OV网的时候,无法判断。
在最后想到如果不是AOV网则不可能有拓扑排
序在这里设一个全局变量count来接受拓扑排序的次数。
若为0则提示改
图非法。
4、主要算法的时间和空间性能的分析。
分析上述拓扑排序算法,如果有向图包含n个顶点和e条弧,则第一个for
循环的时间复杂度为O(n).在单链表中,查找邻接点的时间复杂度为O(e),e为图
中边或弧的数目,加上访问顶点的时间,则每调用一次拓扑排序函数的时间复杂度
为O(n+e)。
算法的总时间复杂度取决于调用拓扑排序函数的次数。
从空间性能
上看,用到两个数组的辅助空间。
以邻接表数据结构建立AOV网的时间复杂度为
O(n)。
邻接表用到表头数组和链表,在单链表中插入一个结点时,需要运行while
语句,所以插入单链表这部分算法的时间复杂度为O(n)。
-5-
5、主要的经验和体会。
我觉的本此课程设计最大的收获就是学会按部就班的完成任务。
首先想好用什么数据结构,主要的思想要明确的认定。
然后选一个简单的例子,按照你选定的思想,一步一步的走程序,才能更早的发现程序还存在那些没有考虑到的缺陷。
特别忌讳的是到写程序的时候,对主要思想和数据结构还不是很确定。
最后在调试程序的阶段,这次我开始用设置断点的方法,一步一步看是否达到你要的理想结果,从而来找出程序中出错的地方。
还有,一种方法也是用一个输出语句,分别放在程序的不同位置,通过输出结果,也可以快速判断出程序不能运行的是那一个语句了。
从而方便你修改,快速得到正确的结果。
还有,这次调试的过程中,我表现的比以前要更有耐心,这也是一个很好的收获。
还有一个更重要的体会就是,遇到不会的地方,我开始习惯到图书馆或是网上去找资料。
这是一个很好的学习过程,它不仅仅是可以解决这个问题,关键是你会从找资料学习的过程中,会学好很多很多意想不到的知识。
可能会比你手头上要学习的知识多得多。
五、测试结果及其分析
1、以图一为例(AOV网)
(1)、输入图,结果如图五所示。
在这里首先输入顶点数和边数,然后输入没条边的起点终点编号和权值。
(2)、输出图的邻接矩阵。
输入图过后按回车键显示图的邻接表,如图六所示。
(3)、在显示完拓扑序列后按回车输出该图的所有拓扑排序序列,如图七所示。
。
-6-
(4)、在不循环使用程序的时候,程序全部运行结束,如图八。
2、在给出的图不是AOV网的时候提示出错。
按图九所示的图,结果如图十所示。
-7-
六、用户使用说明
本程序运行简单只要按照提示的步骤进行操作即可。
要注意的是AOV网里面的每一条边
都是有权值的,虽然这个程序里面用不到,但是为了以后的扩展,
我把权值也加入了,
所以
在输入边的时候需要注意。
其他的都很简单明了。
七、参考文献
[1].
王昆仑,李红。
数据结构与算法。
北京:
中国铁道出版社,
2007年1月。
[2].
谭浩强。
C
程序设计。
清华大学出版社,
2005年7月。
[3].
陆宗骐。
C/C++图像处理编程。
2005年1月。
[4].
程序设计指导。
[5].
曹衍龙,林瑞冲。
C语言实例解析。
人民邮电出版社,
2005年3月。
八、附录
#include"
stdio.h"
malloc.h"
stdlib.h"
#definemax_vertex_num100//顶点的最大个数
typedefstructnode{
//
邻接表中的结点类型
结点编号
指向下一个结点
与表头结点边的信息
定义表头结点数组
顶点信息
指向第一个邻接点
表头结点类型
邻接表类型
邻接表的组
-8-
intvexnum;
//顶点个数表头数
intedgnum;
//边的个数
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));
//定义图
1、请输入顶点数:
"
scanf("
%d"
&
T->
vexnum);
//输入顶点个数
for(i=0;
i<
i++){//初始化表头结点数组
head[i].number=i;
//初始化结点编号
rudu[i]=0;
//将每个节点的荼毒初始化为
head[i].firstarc=NULL;
//将表头结点数组的每个结点的指针域置空
2、请输入边数:
edgnum);
//输入边的个数
3、按起点终点权值的顺序一次输入边的序列:
edgnum;
i++){
请输入第%d条边:
i+1);
p=(ListNode*)malloc(sizeof(ListNode));
j);
//输入这条边起始节点的编号
p->
number);
//输入这条边的终点的编号
info);
//输入这条边的权值
rudu[p->
number]++;
//将插入边中结点的入度加1
next=T->
head[j].firstarc;
head[j].firstarc=p;
//将该结点插入到单链表中创建一条边
returnT;
voidprint(ALGraph*T){//输出图的邻接表
ListNode*p;
inti;
表头数组(该结点的入度)与之有边的节点(该弧的权值)\n"
V%d(%d)"
T->
head[i].number,rudu[i]);
-9-
p=T->
while(p){
---------------->
V%d(%d)"
p->
number,p->
p=p->
Stack*Setnull(){//置栈空
Stack*L;
L=(Stack*)malloc(sizeof(Stack));
L->
top=-1;
return(L);
voidSinsert(Stack*L,intx){//入栈
top=L->
top+1;
data[L->
top]=x;
voidSdelete(Stack*L){//出栈
top-1;
voidSoutput(Stack*L){//输出栈中元素
i++)
V%d"
L->
data[i]);
//把顶点Vi加入到顺序表中
vexnum){//判断顺序表中一种拓扑排序完成Soutput(L);
-10-
voidcaidan(){
\t_____________________________________________________________\n\n"
\t
◇
\t
◇**********
欢迎使用
AOV网拓扑序列显示程序!
**********
\t_____________________________________________________________\n"
voidcaidan1(){
\n\n\n\n\n"
\t_____________________________________________________________\n\n"
★★★★
谢谢使用!
voidmain()
-11-
ALGraph*G;
intn=1;
L=Setnull();
while(n==1){//循环显示拓扑排序
system("
cls"
color2f"
caidan();
注意:
请按下列步骤输入您的AOV网!
\n"
G=create_AdjListGraph();
该图的邻接表为:
print(G);
pause"
以下是该图所有的拓扑序列:
if(rudu[i]==0&
visited[i]==0)topusort(L,G,i)