图生成树实验报告上海电机学院.docx
《图生成树实验报告上海电机学院.docx》由会员分享,可在线阅读,更多相关《图生成树实验报告上海电机学院.docx(13页珍藏版)》请在冰豆网上搜索。
![图生成树实验报告上海电机学院.docx](https://file1.bdocx.com/fileroot1/2022-12/7/b8089c7d-4258-42a4-828a-dab20419693e/b8089c7d-4258-42a4-828a-dab20419693e1.gif)
图生成树实验报告上海电机学院
目录
1课程设计内容1
1.1课程设计目的1
1.2课程设计要求1
1.3课程设计背景1
2概要设计2
2.1程序功能模块结构图2
2.2菜单界面说明2
2.3读入文件说明2
2.4输出孩子链表结构说明2
3详细设计3
3.1邻接矩阵的定义和孩子链表表示法定义3
3.1从文件中读入无向图的结点和边的信息并创建无向图4
3.2输出图的邻接矩阵存储结构7
3.3深度优先遍历8
3.4创建树的孩子链表存储结构9
3.5输出树的孩子链表存储结构10
4遇到的问题与解决方案12
小结13
参考文献14
1课程设计内容
1.1课程设计目的
课程设计题目:
图生成树
随机输入一个无向图,通过深度优先遍历或广度优先遍历输出该图的生成树,并将该生成树以二叉链表形式或孩子链表表示法存储。
若使用二叉链表存储,则对此存储结构进行遍历,如果使用孩子链表表示法,则输出生成树的孩子链表存储结构。
1.2课程设计要求
程序要求实现的功能有:
(1)输入的顶点与弧的信息通过邻接表或邻接矩阵构建一个无向图;
(2)通过图的DFS或BFS过程得到该无向网的一棵生成树。
(3)使用孩子链表表示法或二叉链表存储结构存储生成树。
(4)对存储树的二叉链表进行遍历或者输出生成树的孩子链表存储结构;
1.3课程设计背景
无向图的生成树是在网络和回路分析中经常遇到的重要问题,功能强大、可靠的网络需要有效地传输流量,提供冗余和故障的快速恢复功能。
在第2层网络中,路由协议不可用,生成树协议通过从软件层面修改网络物理拓扑结构来构建一个无环路逻辑转发拓扑结构,提供了物理线路的冗余连接,消除了网络风暴,从而提高网络的稳定性和减少网络故障的发生率。
生成树协议(SpanningTreeProtocol)是在网络有环路时,通过一定的算法将交换机的某些端口进行阻塞,从而使网络形成一个无环路的树状结构。
在实践中理解面向对象语言的设计方式。
2概要设计
2.1程序功能模块结构图
2.2菜单界面说明
本次课程设计,按照要求,需要的完成的有创建图的存储结构,图生成树,树的存储。
为了使菜单更加美化以及程序功能更加完善,本代码增加了输出邻接矩阵功能、输出图生成树的定义、清屏以及退出程序的功能。
2.3读入文件说明
为了使程序具有更大的适用性和方便程序调试,本课程设计代码采取从文件中读入图的顶点和边的信息来创建图的存储结构,为了使生成树的根节点为第一个读入的顶点,在创建树的存储结构中同样使用从同一个文件中读入各顶点建立定点数组。
2.4输出孩子链表结构说明
要清楚地显示图的存储结构则需要将图的孩子链表输出。
由孩子链表存储结构的输出和图的邻接表结构输出类似,故采用同样的方法输出孩子链表。
3详细设计
3.1邻接矩阵的定义和孩子链表表示法定义
要想创建图的邻接矩阵存储结构和生成树的孩子链表存储结构,必须先进行相关的定义。
邻接矩阵和孩子链表表示法定义代码如下:
typedefstructArcCell
{
intadj;//权值
char*info;//指向该边相关信息的指针
}ArcCell,AdjMatrix[20][20];
typedefstruct
{
charvexs[maxSize];//描述顶点的数组
AdjMatrixarcs;//邻接矩阵
intvexnum,arcnum;//图的当前顶点数和边数
}AGraph;
//树的孩子链表表示法定义
typedefstructCTNode
{
intchild;//结点的编号
structCTNode*next;//指针域,指向下一个孩子结点
}*ChildPtr;
typedefstruct
{
chardata;//结点的信息
ChildPtrfirstchild;//指向孩子链表的首个表结点
}CTBox;
typedefstruct//树结构
{
CTBoxnodes[maxSize];//头结点数组
intn;//树的总结点数和树根
}Ctree;
3.1从文件中读入无向图的结点和边的信息并创建无向图
为了节省调试时间,以及方便调试工作,本代码采用了从文件中读取无向图的顶点和边的信息的方法,采用邻接矩阵存储图的方法创建无向图。
无向图结点和边信息g.dat文件如下图3-1所示:
图3-1g.dat文件
读入文件构建无向图的结果如下图3-2所示:
图3-2创建图
实现以上操作的实现代码如下:
voidcreatAGraph(AGraph&G)
{
charv1,v2,vex;
inti,j,w,k;
FILE*fp;
if(NULL==(fp=fopen("g.dat","r")))//打开存储图结点和边信息的文件
{
printf("图的存储文件打开失败,请检查文件g.dat是否存在!
");
exit(-1);
}
printf("请输入图中顶点的个数和边数:
");
fscanf(fp,"%d%d",&G.vexnum,&G.arcnum);
printf("%d%d",G.vexnum,G.arcnum);
printf("\n请输入图中%d个顶点的标志:
\n",G.vexnum);
for(i=0;i{
vex=fgetc(fp);
while(!
isalpha(vex))
{
vex=fgetc(fp);
}
G.vexs[i]=vex;
}
printf("图中的%d个顶点依次为:
\n",G.vexnum);
for(i=0;i{
printf("%2c",G.vexs[i]);
}
for(i=0;ifor(j=0;j{
G.arcs[i][j].adj=int_max;//将邻接矩阵所有元素赋值
G.arcs[i][j].info=NULL;
}
for(k=0;k{
getc(fp);//读取文件中的字符
fflush(stdin);
printf("\n请输入第%d条弧:
",k+1);
fscanf(fp,"%c,%c,%d",&v1,&v2,&w);//输入一条边依附的两点及权值
printf("%c,%c,%d",v1,v2,w);
i=localvex(G,v1);//确定顶点V1和V2在图中的位置
j=localvex(G,v2);
G.arcs[i][j].adj=w;//将权值赋给相应的边
G.arcs[j][i].adj=w;
}
fclose(fp);//关闭文件
}
3.2输出图的邻接矩阵存储结构
为了验证该图的是否创建成功,可以输出该图的邻接矩阵存储结构。
图中0代表两顶点之间没有边,其余数字为两顶点之间边的权值,输出邻接矩阵只需一个两层for()循环即可。
输出该图的邻接矩阵结果如图3-3所示:
图3-3图的邻接矩阵
实现该输出图飞邻接矩阵的代码如下:
voidljjzprint(AGraphG)
{
inti,j;
for(i=0;i{
for(j=0;jprintf("%6d",G.arcs[i][j].adj);
printf("\n");
}
}
3.3深度优先遍历
要想实现图生成树的目的,必须通过某种方法遍历所有顶点然后记录其路径,本程序采用深度优先遍历(DFS)的方法实现生成树。
.程序中定义了外部变量,目的是记录遍历该图的遍历路径,通过这些顶点记录来创建树的存储结构。
深度优先遍历的结果如下图3-4所示:
图3-5图的深度优先遍历
实现深度优先遍历的代码如下:
voidDFS_Matrix(AGraphG,intk,intvisited[])
{
intj;
visited[k]=1;
printf("%3c",G.vexs[k]);//输出遍历的路径
for(j=0;j{
if((G.arcs[k][j].adj!
=0)&&0==visited[j])
{
a[m]=k;//记录遍历顶点位置
m++;
b[t]=j;//记录遍历顶点位置
t++;
DFS_Matrix(G,j,visited);
}
}
}
voidDFSTra_Matrix(AGraphG)
{
intk;
intvisited[maxSize]={0};//设置顶点标记
m=0;
t=0;
for(k=0;k{
if(visited[k]==0)//若编号为k的顶点未被访问过,则以该顶点为出发顶点
DFS_Matrix(G,k,visited);
}
}
3.4创建树的孩子链表存储结构
通过图的深度优先遍历生成了相应的树,由于采用外部变量记录了遍历各顶点的先后顺序,即通过两个数组分别存放了除最后一个顶点外的所有顶点编号以及除第一个顶点外的所有顶点编号。
创建该树同样采用读取文件的方式,由于创建图的存储结构时,就对所有顶点进行了编号,第一个读入的顶点就是生成树的根节点,因此,创建该树同样读取了g.dat中的顶点信息,将读入的顶点信息放入一个数组中,然后,将外部变量记录的顶点编号插入到数组(和邻接表的构造方法类似)就创建了孩子链表存储结构。
创建树的存储结构如图3-5所示:
图3-5创建树的存储结构
树的孩子链表存储实现代码如下:
voidCreat_Tree(Ctree&T)
{
inti;
charvex;
ChildPtrp;
FILE*fp;
if(NULL==(fp=fopen("g.dat","r")))
{
printf("树的存储文件打开失败,请检查文件g.dat是否存在!
");
exit(-1);
}
printf("请输入图中顶点的个数:
");
fscanf(fp,"%d",&T.n);
printf("%d",T.n);
for(i=0;i{
vex=fgetc(fp);
while(!
isalpha(vex))
{
vex=fgetc(fp);
}
T.nodes[i].data=vex;
T.nodes[i].firstchild=NULL;
}
printf("图中的%d顶点依次为:
\n",T.n);
for(i=0;iprintf("%3c",T.nodes[i].data);
for(m=0,t=0;m<6&&t<5;m++,t++)
{p=(ChildPtr)malloc(sizeof(ChildPtr));
p->child=b[t];
p->next=NULL;
p->next=T.nodes[a[m]].firstchild;
T.nodes[a[m]].firstchild=p;
}
fclose(fp);
}
3.5输出树的孩子链表存储结构
为了能清楚的知道该树的结构,所以需要输出生成树的存储结构,输出孩子链表存储结构和输出图的邻接表存储结构类似。
树的孩子链表存储结构如图3-6所示:
图3-6输出树的孩子链表存储结构
实现输出树的孩子链表存储结构的代码如下:
voidOutCtree(CtreeT)
{
inti;
ChildPtrp;
printf("\n孩子链表中的数据如下:
\n");
printf("%3s%s链表结点(顶点下标边的权值指针)\n","下标","顶点");
for(i=0;i{
printf("%3d|%c",i,T.nodes[i].data);
p=T.nodes[i].firstchild;
while(p)
{
printf("□---%d",p->child);
p=p->next;
}
printf("∧\n");
}
}
4遇到的问题与解决方案
在编写代码时遇到的一些问题及各个问题的解决方案如下:
(1)在创建图的邻接矩阵存储结构时,无法从文件中读取字符?
解决:
未从文件中读入顶点的数,在文件中添加顶点数,然后读取即可。
(2)怎样将遍历的顶点编号记录下来,?
解决:
记录遍历路径并且要利用此记录创建树的存储结构,则设置全局变量,存储遍历图时经过的各顶点的编号。
(3)怎样让生成树的根节点为图遍历的第一个节点?
解决:
由于图的各顶点的信息存在文件中,同样的,可以从该文件中读取各顶点,则建立的孩子链表即为以第一个图遍历的顶点为根节点的存储结构。
(4)怎样利用记录的顶点编号创建树的存储结构?
解决:
因为记录的顶点编号存储在全局变量中,而且在创建孩子链表存储结构时已经从文件中读取定点数组,故根据全局变量带回的顶点编号记录,即可创建孩子链表(类似于邻接表的创建)。
(4)怎样将树的存储结构输出?
解决:
由于孩子链表创建过程类似于图的邻接表创建,故在输出过程中也具有很多的相似性,故可以根据图的邻接表输出方法输出树的孩子链表存储结构。
小结
这次课程设计,感受颇多,选好题目后我就开始了自己的课设之旅,一开始没有什么思绪,由于对图和树的知识掌握的不够全面,所以刚开始觉得比较吃力,在图书馆查阅了一些资料以及上网搜索了一些资料后我开始了自己的第一次尝试,利用邻接矩阵的方法创建图的存储结构,利用深度优先遍历(DFS)搜索出遍历路径,但是不知道怎样记录路径以及怎样创建树,而且所有的顶点信息及边的信息是从终端输入,在老师的指导下,我选择了从文件中读入图的相关信息来创建图的存储结构,由于在之前的报告中有类似的操作,于是我在实验报告的基础上作了第二次努力,采用邻接表存储图,采用深度优先遍历生成树,采用孩子链表存储结构存储生成树,就这样,利用一下午的时间,终于完工了。
但是在基于邻接表深度优先遍历时,有可能生成的不是树而是深林,这又让我麻烦了好久,最终我决定放弃邻接表存储结构,选用邻接矩阵,又发现孩子链表存储结构和图的邻接表存储结构有类似之处,于是我又开始了第三次的探索,这一次,通过定义全局变量记录生成树节点的编号,然后创建数的孩子链表存储结构,继而输出孩子链表存储结构,就这样,本次的课程设计在一次次的探索中诞生了。
通过本次课程设计,让我掌握了图和树的许多知识,也明白了自己有许多的只是还没掌握,在编程的大海中,我学到的只不过是一滴水的大小,冰冻三尺非一日之寒,前方的路还有许多困难需要克服,我需要更加努力。
参考文献
[1]率辉编著.数据结构高分笔记北京:
机械工业出版社,2012.6
[2]严蔚敏,陈文博编著.数据结构及应用算法教程北京:
清华大学出版社,2011.5
[3]沈华编著.数据结构及应用北京:
机械工业出版社,2010.10