数据结构课程设计论文.docx
《数据结构课程设计论文.docx》由会员分享,可在线阅读,更多相关《数据结构课程设计论文.docx(29页珍藏版)》请在冰豆网上搜索。
数据结构课程设计论文
《数据结构(C语言版)》
——课程设计报告——
院系:
信息科学与技术学院
设计题目:
无向网邻接矩阵的建立、
无向网的遍历与普里姆算法的实现,
有向图邻接表的建立
专业班级:
学号:
姓名:
指导教师:
设计日期:
2011年12月21日
摘要:
仅仅学习理论而脱离实践,往往会变成“纸上谈兵”。
本课程设计用C语言作基础,以源程序的形式具体实现了数据结构中,无向网邻接矩阵的建立、无向网邻接矩阵的遍历、普里姆算法求最优树以及有向图邻接表的建立。
关键词:
数据结构邻接矩阵遍历普里姆算法邻接表
目录
1.课程设计的内容----------------------------------------------------------------------3
1.1无向网部分---------------------------------------------------------------------3
1.2有向图部分---------------------------------------------------------------------3
2.课程设计中所用的函数说明-------------------------------------------------------3
2.1无向网部分---------------------------------------------------------------------3
2.2有向图部分---------------------------------------------------------------------3
3.算法的设计思想----------------------------------------------------------------------3
3.1邻接矩阵的建立---------------------------------------------------------------3
3.2邻接矩阵的初始化------------------------------------------------------------4
3.3邻接矩阵的输出---------------------------------------------------------------5
3.4邻接矩阵的遍历---------------------------------------------------------------5
3.5普里姆最小生成树------------------------------------------------------------6
3.6邻接表的建立-----------------------------------------------------------------9
4.运行结果-------------------------------------------------------------------------------12
4.1无向网部分---------------------------------------------------------------------12
4.2有向图部分---------------------------------------------------------------------14
5.时间复杂度说明----------------------------------------------------------------------16
6.心得体会-------------------------------------------------------------------------------17
参考文献---------------------------------------------------------------------------------18
附录:
C语言代码清单----------------------------------------------------------------19
1.课程设计的内容
1.1无向网部分:
无向网邻接矩阵的建立,无向网邻接矩阵的遍历,普里姆算法(最优生成树)的实现。
1.2有向图部分:
有向图邻接表的建立。
2.课程设计中所用的函数说明
2.1无向网部分:
函数名称
函数功能
creatMGraph
创建一张无向网的邻接矩阵
printMGraph
输出一张无向网的邻接矩阵
DFS
遍历一张无向网
MiniSpanTree_PRIM
实现基于无向网的普里姆算法,即生成最优树
2.2有向图部分:
函数名称
函数功能
creatALGraph
创建一张有向图的邻接表
printfALGraph
输出一张有向图的邻接表
3.算法的设计思想
3.1邻接矩阵的建立
邻接矩阵表示顶点之间的相邻关系,设
,是具有n个顶点的网,则G的邻接矩阵是具有如下性质的n阶方阵:
(1)如果顶点
和
相连,则
赋值为权值,否则为无穷大(本课设中采用最大数代替无穷大)。
(2)对于无向网,
。
用邻接矩阵表示法表示网,除了存储邻接矩阵外,通常还需要用一个顺序表来存储顶点信息。
其具体C语言代码如下:
#definevexnum6/*图的顶点数目*/
#definearcnum10/*图的边(弧)数目*/
intvisited[vexnum]={0};/*顶点是否被遍历的标志数组*/
typedefstruct{/*创建一个用邻接矩阵表示的图*/
intvexs[vexnum];/*顶点信息*/
floatarcs[vexnum][vexnum];/*邻接矩阵*/
}MGraph;
3.2邻接矩阵的初始化
由于无向网的邻接矩阵是对称的,故可采用压缩存储的方法,仅存储下三角阵(不包括对角线上的元素)中的元素。
显然,邻接矩阵表示法的空间复杂度
。
邻接矩阵的建立过程如下代码所示:
voidcreatMGraph(MGraph*m)/*向一个用邻接矩阵表示的图中输入数值*/
{
inti,j,k;
floatwqd;/*因为后面要应用普里姆算法,故设置“无穷大”为wqd*/
floatw;
for(i=0;i{
printf("请输入顶点(%d):
",i);/*输入第i号顶点的数据*/
scanf("%d",&m->vexs[i]);
}
printf("请输入权值最大范围0--:
");/*确定最大值,即“无穷大”,一般输入一个比最大值再大一些的数字作为“无穷大”*/
scanf("%f",&wqd);
for(i=0;ifor(j=0;jm->arcs[i][j]=wqd;/*邻接矩阵初始化,全部赋值无穷大*/
for(k=0;k{
inta=arcnum;
intb=(vexnum-1);
printf("请输入边(1-%d)与权值(顶点0--%d):
",a,b);/*提示信息*/
scanf("%d%d%f",&i,&j,&w);/*读入边
上的权w*/
m->arcs[i][j]=w;
m->arcs[j][i]=w;
}
}
3.3邻接矩阵的输出
本课设采用行列式的形式输出邻接矩阵,具体C语言代码如下:
voidprintMGraph(MGraph*m)
{
inti,j;
printf("");/*格式*/
for(i=0;iprintf("%d(%d)",m->vexs[i],i);/*输出格式为“顶点信息(顶点编号)”*/
printf("\n");
printf("---");
for(i=0;iprintf("------------");/*下横线*/
printf("\n");
for(i=0;i{
printf("%d(%d)|",m->vexs[i],i);
for(j=0;jprintf("%f",m->arcs[i][j]);/*依次输出每一个顶点与其他顶点的关系*/
printf("\n");
}
}
3.4邻接矩阵的遍历
对于图的遍历,和树的遍历类似,也是从某个顶点出发,沿着某条搜索路径对图中所有顶点做一次访问。
若给定的图是连通图,则从图中任一顶点出发顺着边可以访问到该图的所有顶点。
又因为图中任一顶点都可能和其余顶点相邻接,故在访问了某个顶点之后,可能顺着某条回路又回到了该顶点。
为了避免重复访问同顶点,必须记住每个顶点是否被访问过。
为此,我们已经在前面设置了向量intvisited[vexnum]={0}来标识。
它的初值为0,一旦访问了顶点
,便将顶点
置为1。
邻接矩阵遍历的具体C语言代码如下:
voidDFS(MGraph*m,inti)/*从邻接矩阵的第i个顶点开始遍历*/
{
intj;
visited[i]=1;/*i号顶点标识为1,代表已经遍历过了*/
printf("%d-",i);/*输出已经遍历的顶点序号*/
for(j=0;jif((m->arcs[i][j]!
=0)&&(visited[j]==0))/*只有当顶点i与另外一个顶点j相邻且j顶点没有被遍历过的时候,才采用递归遍历*/
DFS(m,j);/*递归遍历j顶点*/
printf("\b");/*退格键,去掉最后一个“-”*/
printf("\n");
}
3.5普里姆最小生成树
不少图论问题在计算时,往往首先必须求出一棵最小生成树。
设
是一个无向图,如果
是由G的全部顶点及一部分边组成的子图,并且T是树(连通,没有环的图),则称T是G的一棵生成树。
一个连通图G的生成树不是唯一的,从不同的顶点出发进行遍历,得到不同的生成树。
对于连通网络
,边是带权的,因而G的生成树的各边也是带权的。
我们把生成树各边的权值总和称为生成树的权,并把权最小的生成树称为G的最小生成树。
最小生成树有许多重要的应用。
令图G的顶点表示城市,边表示连接两个城市之间的通信线路。
n个城市之间最多可设立的线路有
条,把n个城市连接起来至少要有n-1条线路,则图G的生成树表示了建立通信网络的可行方案。
如果给图中的边都赋予权,则这些权可表示两个城市之间通信线路的长度或建造代价,那么,如何选择一条线路,使得建立的n-1条通信网络其线路的总长度最短或总代价最小呢?
这就需要构造该图的一棵最小生成树。
本课设采用普里姆算法生成一个无向网的最优生成树。
基本思想为:
在所有
,
的边
中找到一条代价最小的边
并入最小生成树边的集合
中,同时
并入
,直至
为止。
为此,需要特别说明一个辅助矩阵——closedge,本课设中closedge的定义是这样的:
closedge为一个结构体数组,每一个结构体由两部分组成,一个是U集合中对应于第i号元素的边的集合中,最小权值的顶点序号,实际上就是两个顶点(i和closedge.adjvex)所确定的边;另一个是该边上的权值。
数组closedge代码如下:
typedefstruct{/*辅助功能,用来选择最小生成树的边*/
intadjvex;/*顶点序号*/
floatlowcost;/*最小权值*/
}closedge;
首先,我们从邻接矩阵中选一个顶点开始查找,设该顶点序号为u,将辅助数组closedge中的所有元素依次赋值为与顶点u对应的边的信息,而顶点u的lowcost的值为0,表明此顶点已经并入集合U之中了。
然后,我们搜索数组closedge,从中找到权值最小的边所对应的序号,此序号所表示的顶点即为下一个进入集合U的顶点。
接下来,我们对剩下的vexnum-1个顶点进行同样的逐个查找,依次找出每个顶点所对应的,当前集合U中的(集合U是不断变化的),与之所形成的边的最小值,将最小值赋给lowcost,对应集合U中的顶点序号赋给adjvex。
依次往复,即可找出所有最有树的边。
但是,我发现书中的算法出现了一个小小的错误,即175页中算法7.9正数第二个for循环中,i中仅剩下一个顶点元素,所以无需再进行比较,直接插入最优树即可,也就是说,最后一次循环中,数组closedge仅剩下一个顶点的lowcost不等于0,其所对应的adjvex即为需要连接进入集合U的顶点。
相应的,在书中算法7.9的结尾,也应当编写一段类C语言说明最后一次循环的情况。
具体普里姆算法C语言代码如下:
voidMiniSpanTree_PRIM(MGraph*m,intu)/*u为开始顶点编号*/
{
intk,i,j;
closedgecs[vexnum];
for(j=0;jif(j!
=u)
{
cs[j].lowcost=m->arcs[u][j];
cs[j].adjvex=u;
}
cs[u].lowcost=0;/*表明已进入顶点集U*/
floatsum=0.0;/*用于计算最小权值总和*/
for(i=1;i<(vexnum-1);i++)/*对剩余的vexnum-2个顶点(循环次数)*/
{
intp;/*找出数组cs中的最小权值所对的序号(lowcost为MAX的除外)*/
floatmax=0.0;
for(p=0;pif(maxmax=cs[p].lowcost;
floatmin=max;
for(p=0;pif((cs[p].lowcost!
=0)&&(min>cs[p].lowcost))
{min=cs[p].lowcost;
k=p;/*下一个进入集合U的顶点编号,即V-U中的最小权值顶点*/
}
printf("%d---%d最小权值为%f",cs[k].adjvex,k,cs[k].lowcost);
sum=sum+cs[k].lowcost;/*求权值*/
printf("\n");
cs[k].lowcost=0;/*表明已进入顶点集U*/
for(j=0;jif(m->arcs[k][j]{
cs[j].adjvex=k;/*相对于上一个u而言,k所对应的权值更小*/
cs[j].lowcost=m->arcs[k][j];
}
}
for(i=0;iif(cs[i].lowcost!
=0)k=i;/*最后一个进入集合U的顶点序号*/
printf("%d---%d最小权值为%f",cs[k].adjvex,k,cs[k].lowcost);
printf("\n");
printf("该最小生成树权值总和为:
%f",sum);
printf("\n");
}
3.6邻接表的建立
邻接表是把图的每个顶点的邻接顶点用链接表表示,这种表示方法类似于树的孩子链表表示法。
为此,需要建立一个顺序存储n个顶点的表,针对图G中的每个顶点
,该方法把所有邻接于
的顶点
链成一个单链表,这个单链表就称为顶点
的邻接表。
邻接表中每个表结点均有两个域,其一是邻接域,用以存放与
相邻接的顶点
的序号;其二是链接域,用来指向与顶点
相邻的下一个顶点,这样,就可以将邻接表的所有表结点链接在一起。
同时,为每个顶点
的邻接表设置一个具有两个域的表头结点,一个是顶点域,用来存放顶点
的信息。
另一个是指针域,用于存入指向
的邻接表中第一个表结点的头指针。
其具体C语言代码如下:
#definevexnum6
#definearcnum5
typedefstruct{/*表头结点*/
intadjvex;
structArcnode*next;
}Arcnode;
typedefstructVnode{/*表结点*/
intdata;
structArcnode*next;
}Vnode;
typedefstruct{/*用邻接表表示的有向图*/
VnodeGmap[vexnum];
}ALGraph;
/*————————————邻接表的建立————————————*/
voidcreatALGraph(ALGraph*m)
{
inti,j,k;
for(i=0;i{
m->Gmap[i].data=0;
m->Gmap[i].next=NULL;
}
for(i=0;i{
printf("请输入顶点(%d)数值:
",i);
scanf("%d",&m->Gmap[i].data);
}
for(k=0;k{
inta=(vexnum-1);
printf("请输入有向边“弧头弧尾”(以0为起点,0-%d):
",a);
scanf("%d%d",&i,&j);
Arcnode*q=(Arcnode*)malloc(sizeof(Arcnode));/*开辟一个新的结点空间,并且从表头插入*/
q->adjvex=i;
q->next=m->Gmap[j].next;
m->Gmap[j].next=q;
}
}
/*——————————邻接表的输出————————————*/
voidprintfALGraph(ALGraph*m)
{
inti;
for(i=0;i{
printf("%d(%d)|",m->Gmap[i].data,i);/*输出顶点信息,顶点序号*/
Arcnode*p=m->Gmap[i].next;
while(p!
=NULL)/*从表头输出至表尾*/
{
printf("-%d-",p->adjvex);
p=p->next;
}
printf("\b");/*退格键,去掉最后一个“-”*/
printf(“\n”);
}
}
4.运行结果
运行环境:
MicrosoftVisualC++
程序语言:
C语言
下面,我们来检验一下程序能否正确运行。
4.1无向网部分
如图所示,为一张无向网。
主程序main()编写如下:
voidmain()
{
intv;
MGraphg={{0},{0}};/*无向网的初始化*/
MGraph*y=&g;
creatMGraph(y);/*输入无向网的信息*/
printf("\n");
printf("无向网的邻接矩阵如下:
");
printf("\n");
printMGraph(y);/*输出无向网的邻接矩阵*/
printf("无向网的遍历如下:
");
printf("\n");
for(v=0;vif(!
visited[v])DFS(y,v);
printf("\n");
printf("依据普里姆算法,得该图的最小生成树为:
");
printf("\n");
intu=0;/*从序号为0的顶点开始普里姆算法*/
MiniSpanTree_PRIM(y,u);/*无向网的最小生成树*/
}
运行步骤:
1.编译无误,运行,界面如下所示
2.输入顶点
3.输入“无穷大”值,譬如90
4.输入各条边的信息
5.运行结果为
因为程序设置的是浮点型数据(float),故输出结果中会有一些误差。
其余的结果皆正确。
4.2有向图部分
如图所示,为一张有向图
主程序main()编写如下:
voidmain()
{
ALGraphm={{0,NULL}};/*有向图的初始化*/
ALGraph*g=&m;
creatALGraph(g);/*输入有向图的信息*/
printf("该有向图的邻接表如下所示:
");
printf("\n");
printfALGraph(g);/*输出邻接表*/
}
运行步骤:
1.编译无误,运行,界面如下所示
2.输入顶点信息
3.输入各条边的信息
4.运行结果
与原图对照,可知结果正确。
5.时间复杂度说明
构造一个具有n个顶点和e条边的无向网MGraph的时间复杂度是
,其中对邻接矩阵MGraph.arcs的初始化耗费了
的时间。
假设网中有n个顶点,对用邻接矩阵表示的无向网进行遍历所需的时间复杂度为
。
对普里姆算法而言,第一个进行初始化的循环语句的频度为n,第二个循环语句的频度为n-1。
其中有2个内循环:
其一是在cs[v].lowcost中求最小值,其频度为n-1;其二是重新选择具有最小代价的边,其频度为n。
由此,普里姆算法的时间复杂度为
,与网中的边数无关,因此适用于求边稠密的网的最小生成树。
建立有向图的邻接表的时间复杂度为
。
6.心得体会
数据结构这门课的思想性很强,它包纳了线性表、树、图等等逻辑结构。
但学习这门课,光靠上课听讲还远远不能满足学习的需要,必须亲自编写源程序,不断地调试、运行、反复地思考,才能加深对本学科的认识,强化对所学知识的应用。
我认为,数据结构的相关知识和C语言的学习完全可以融为一体,一方面学习算法思想,另一方面用语言去实践。
相得益彰,趣味盎然。
参考文献
严蔚敏吴伟民.数据结构(C语言版).北京:
清华大学出版社.1992年4月
徐孝凯贺桂英.数据结构(C语言描述).北京:
清华大学出版社.2004年2月
李克清夏祥胜崔洪芳.数据结构——C语言描述.武汉:
华中科技大学出版社.2004年12月
附录:
C语言代码清单
1.无向网部分
#include
#include
#definevexnum6
#definearcn