最小生成树课程设计报告.docx
《最小生成树课程设计报告.docx》由会员分享,可在线阅读,更多相关《最小生成树课程设计报告.docx(14页珍藏版)》请在冰豆网上搜索。
最小生成树课程设计报告
数据结构课程设计报告
题目克鲁斯卡尔算法求最小生成树
学院计算机与信息工程系______
专业网络工程_
学号______________
姓名_________
指导教师_________
完成日期_____2010-9-3___________
一.需求分析
1.可以用连通网来表示n个城市间可能设置的通信网络,其中网的顶点表示城市,边表示两城市之间的路线,边的权值表示相应的费用。
对于n个顶点的连通网可以建立许多不同的生成树,每一棵生成树都可以是一个通信网。
现在,我们要选择这样一棵生成树,它使总的费用最少,这棵树就是最小生成树。
一棵生成树的费用就是树上各边的费用之和。
2.本程序的目的是要建设一个最经济的通信网,根据用户输入的网,输出相应的最小生成树。
在这里城市以及两城市之间的费用都用整型数来代替。
3.程序执行的命令包括:
(1)利用克鲁斯卡尔算法求最小生成树。
(2)构造最小生成树中的连通分量。
(3)权值应存放在定义的数组中。
(4)输入城市个数。
(5)输出费用最少的生成树。
(6)结束。
4.测试数据
用户自定义输入城市个数,输入结束后回车即显示生成的最小生成树及最小开销。
二.概要设计
1:
抽象数据类型MFSet的定义:
ADTMFSet{
数据对象:
若设S是MFSet型的集合,则它由n(n>0)个子集Si(i=1,2...,n)构成,每个子集的成员代表在这个子集中的城市。
数据关系:
S1US2US3U...USn=S,Si包含于S(i=1,2,...n)
Init(n):
初始化集合,构造n个集合,每个集合都是单成员,根是其本身。
rank数组初始化0
Find(x):
查找x所在集合的代表元素。
即查找根,确定x所在的集合,并路径压缩。
Merge(x,y):
检查x与y是否在同一个集合,如果在同一个集合则返回假,否则按秩合并这两个集合并返回真。
}
2:
主程序:
intmain()
{
初始化;
while(条件)
{
接受命令;
处理命令;
}
return0;
}
3:
抽象数据类型图的定义如下:
ADTGraph{
数据对象V:
V是具有相同特性的数据元素的集合,成为顶点集。
数据关系R:
R={VR}
VR={|v,w∈V且P(v,w),表示从v到w的弧,谓词P(v,w)定义了弧的意义或信息}
基本操作P:
CreateGraph(&G,V,VR);
初始条件:
V是图的顶点集,VR是图中弧的集合。
操作结果:
按V和的VR定义构造图G。
DestoryGraph(&G);
初始条件:
图G存在。
操作结果:
销毁图G。
LocateVex(G,u);
初始条件:
图G存在,u和G中是顶点有相同特征。
操作结果:
若G中存在顶点u,则返回该顶点在图中位置;否则返回其他信息。
GetVex(G,v);
初始条件:
图G存在,v是G中某个顶点。
操作结果:
返回v的值。
PutVex(&G,v,value);
初始条件:
图G存在,v是G中某个顶点。
操作结果:
对V赋值value,
FirstAdjVex(G,v);
初始条件:
图G存在,v是G中某个顶点。
操作结果:
返回v的第一个邻接顶点。
若顶点在G中没有顶点,
则返回“空”。
NextAdjVex(G,v,w);
初始条件:
图G存在,v是G中某个顶点,w是v的邻接顶点。
操作结果:
返回v的(相对于w的)下一个邻接顶点。
若w是v的最后一个邻接顶点,则返回“空”。
InsertVex(&G,v);
初始条件:
图G存在,v和途中顶点有相同特征。
操作结果:
在图G中添加新顶点v。
DeleteVex(&G,v);
初始条件:
图G存在,v是G中某个顶点。
操作结果:
删除G中顶点v及其相关的弧。
InsertArc(&G,v,w);
初始条件:
图G存在,v和w是G中两个顶点。
操作结果:
在G中添加弧,若G是无向的,则还增添对称弧。
DeleteArc(&G,v,w);
初始条件:
图G存在,v和w是G中两个顶点。
操作结果:
在G中删除弧,若G是无向的,则还删除对称弧。
DFSTravrese(G,Visit());
初始条件:
图G存在,Visit是顶点的应用函数。
操作结果:
对图进行深度优先遍历。
在遍历过程中对每个顶点调用函数Visit一次且仅一次。
一旦Visit()失败,则操作失败。
BFSTravrese(G,Visit());
初始条件:
图G存在,Visit是顶点的应用函数。
操作结果:
对图进行广度优先遍历。
在遍历过程中对每个顶点调用函数Visit一次且仅一次。
一旦Visit()失败,则操作失败。
}ADTGraph
4:
抽象数据类型树的定义如下:
ADTTree{
数据对象D:
D是具有相同特性数据元素的集合。
数据关系R:
若D为空集,则称为空树;若D仅含一个元素数据,则R为空集,否则R={H},H是如下二元关系:
(1)在D中存在唯一的称为根的数据元素root,它在关系H下无前驱;
(2)若D-{root}≠
,则存在D-{root}的一个划分D1,D2,…,Dm(m>0),对任意j≠k(1≤j,k≤m)有Dj∩Dk=
,且对任意的I(1≤i≤m),惟一存在数据元素xi∈Di有∈H;
(3)对应于D-{root}的划分,H-{,…,}有惟一的一个划分H1,H2,…,Hm(m>0),对任意j≠k(1≤j,k≤m)有Hj∩Hk=
,且对任意I(1≤i≤m),Hi是Di上的二元关系,(Di,{Hi})是一棵符合本定义的树,称为跟root的子树。
基本操作P:
InitTree(&T);
操作结果:
构造空树T。
DestoryTree(&T);
初始条件:
树T存在。
操作结果:
销毁树T。
CreateTree(&T,definition);
初始条件:
definition给出树T的定义。
操作结果:
按definition构造树T。
ClearTree(&T);
初始条件:
树T存在。
操作结果:
将树T清为空树。
TreeEmptey(T);
初始条件:
树T存在。
操作结果:
若T为空树,则返回TRUE,否则FALSE。
TreeDepth(T);
初始条件:
树T存在。
操作结果:
返回T的深度。
Root(T);
初始条件:
树T存在。
操作结果:
返回T的跟。
Value(T,cur_e);
初始条件:
树T存在,cur_e是T中某个结点。
操作结果:
返回cur_e的值。
Assign(T,cur_e,value);
初始条件:
树T存在,cur_e是T中某个结点。
操作结果:
结点cur_e赋值为value。
Parent(T,cur_e);
初始条件:
树T存在,cur_e是T中某个结点。
操作结果:
若cur_e是T的非根结点,则返回它的双亲,否则函数值为“空”。
LeftChild(T,cur_e);
初始条件:
树T存在,cur_e是T中某个结点。
操作结果:
若cur_e是T的非叶子结点,则返回它的最左
子,否则返回“空”。
RightSibling(T,cur_e);
初始条件:
树T存在,,cur_e是T中某个结点。
操作结果:
若cur_e有右兄弟,则返回它的右兄弟,否则函数值为“空”。
InsertChild(&T,&p,I,c);
初始条件:
树T存在,P指向T中某个结点,1≤i≤p所指向的结点度数+1,非空树c与T不相交。
操作结果:
插入c为T中p指结点的第i棵子树。
DeleteChild(&T,&p,i);
初始条件:
树T存在,p指向T中某个结点,1≤i≤p指结点的度。
操作结果:
删除T中p所指结点的第i棵子树。
TraverseTree(T,Visit());
初始条件:
树T存在,Visit是对结点操作的应用函数。
操作结果:
按某种次序对T的每个结点调用函数visit()一次且至多一次。
一旦vista()失败,则操作失败。
}ADTTree
5:
本程序包括两个模块,调用关系比较简单:
(1)主程序模块
(2)带权无向图模块。
程序各模块之间的调用关系如下:
主程序模块
带权无向图模块
三.详细设计
#include
#include
#include
usingnamespacestd;
#defineMOD101
#defineMAXN30
intset[MAXN];
intrank[MAXN];
typedefstructMintree
{//最小生成树结构体定义
intx,y;
intdis;
}Mintree;
Mintreemap[MAXN],mst[MAXN];
boolcmp(constMintreea,constMintreeb)
{
returna.dis}//快排比较函数
voidInit(intn)
{//初始化为n个集合
inti;
for(i=0;i{
set[i]=i;
rank[i]=0;
}
}
intFind(intx)
{//查找该集合的根
intr=x;
while(set[r]!
=r)
r=set[r];
inti=x,j;
while(i!
=r)
{
j=set[i];
set[i]=r;
i=j;
}//路径压缩
returnr;
}
boolMerge(intx,inty)
{//合并集合函数
intfx=Find(x);
intfy=Find(y);
if(fx!
=fy)
{//两端点不在一个集合,合并并返回真
if(rank[fx]>rank[fy])
{
set[fy]=fx;
}
elseif(rank[fx]{
set[fx]=fy;
}
else
{
set[fx]=fy;
rank[fy]++;
}//按秩合并
returntrue;
}
returnfalse;
//两端点原本在一个集合,返回假。
}
intmain()
{
intn,i,j,k,num;
intfx,fy;
intans;
srand((unsigned)time(NULL));//随机种子函数
printf("请输入城市数量n=");
while(scanf("%d",&n)!
=EOF)
{
Init(n);
puts("--------------------------------\n");
printf("随机生成%d条通信网络费用信息:
\n",n*(n-1)/2);
k=0;
for(i=0;i{
for(j=i+1;j{
map[k].x=i;
map[k].y=j;
map[k].dis=rand()%MOD;//生成伪随机数
printf("\n<%-2d,%-2d>\t%-2d\n",map[k].x,map[k].y,map[k].dis);
k++;
}
}
puts("--------------------------------\n");
sort(map,map+k,cmp);
//按边从小到大的顺序进行快速排序
ans=0;
num=0;
printf("得到开销最小的通信网络如下:
\n");
for(i=0;i{
if(Merge(map[i].x,map[i].y))
{//边的端点不在一个集合,则合并之
ans+=map[i].dis;//最小生成树的所有边的带权和
printf("\n<%-2d,%-2d>\t%-2d\n",map[i].x,map[i].y,map[i].dis);
num++;
if(num==n-1)
break;
}
}
printf("\n并且最小开销为:
%d\n\n",ans);
system("pause");
system("cls");
printf("请输入城市数量n=");
}
return0;
}
四.调试分析
1.在本程序中用存储边(带权)的数组表示图,而不是用邻接矩阵的数组
表示法或邻接表。
为了让读者能一目了然看清结果,在终端输出从顶点0开始的最短路径时,设置了输出值的宽度。
2.为了简便起见,程序中的顶点、边的权值都设置成了整形,顶点的标记从
0开始,即用数字代替城市名称。
3.在写程序时遇到很多有关专业名词的C语言编译,没有完全套用书上的固
有解释,而是按照自己有限的英语词汇的理解去编译的。
4.由于克鲁斯卡尔算法是一种按权值的递增次序选择合适的边来构造最小
生成树的方法,构造最小生成树的时间复杂度为O(eloge),e为网中边的数目,
所以其时间复杂度与边有关,它适用于稀疏图。
普里姆算法也是求最小生成树的
基本算法,其时间复杂度为O(n
),与网中的边数无关,而与定点数有关,故其
适用于求边稀疏的网的最小生成树。
所以说,两种算法各有优势。
5.通过求最小生成树,进一步掌握了图的含义,掌握了克鲁斯卡尔算法的基
本思想及流程。
知道了克鲁斯卡尔算法与普里姆算法的区别与联系。
通过本次
课程设计,锻炼了我们的实际操作能力,培养了我们严密的思维和严谨的态度。
五.用户手册
1.本程序的运行环绕为XP下的仿真DOS系统,执行文件为Minitree.exe。
2.进入演示程序后即显示文本方式的用户界面:
3.用户按提示输入后,程序即显示运行结果。
六.测试结果
七.附录
源程序文件名清单:
Mintree.C//主程序文件