最小生成树 C++副本.docx
《最小生成树 C++副本.docx》由会员分享,可在线阅读,更多相关《最小生成树 C++副本.docx(15页珍藏版)》请在冰豆网上搜索。
最小生成树C++副本
2012届课程(设计)论文
题目
最小生成树
专业班级
信息与计算科学
(2)班
学号
学生姓名
指导教师
指导教师职称
讲师
学院名称
理学院
完成日期:
2014年6月18日
目录
目录I
摘要II
AbstractIII
前言IV
第1章课题背景1
1.1问题背景1
1.2基础知识1
1.3意义2
1.4文献综述2
第2章基于最小生成树的算法及思想3
2.1算法概述3
2.2数据结构设计3
第3章功能函数实现4
3.1生成树的输出函数建立4
3.2生成树的升序排序4
3.3克鲁斯卡尔算法函数5
3.4程序与所实现的最小生成树方案6
3.5程序的优缺点及改进11
第4章总结12
致谢13
参考文献14
附录15
摘要
最小生成树问题和求路线最短问题类似,为了解决这样的问题,我们采用了克鲁斯卡尔算法思想。
图论中其中包括有无向图、有向图,通过程序的结果,解决了关于无向图的最小生成树问题。
关键词:
克鲁斯卡尔算法、最小生成树
Abstract
Theproblemofminimumspanningtreeissimilartotheproblemoftheshortestroute,weusetheKruskalalgorithminordertosolvethisproblem.Ingraphtheory,includingtheundirectedgraph,adirectedgraph,wehavesolvedtheundirectedgraphoftheminimumspanningtreeproblembytheprogramresults.
Keywords:
Kruskalalgorithm、minimumspanningtree
前言
本文解决了通过二叉树的链表方式存储数据和克鲁斯卡尔算法并得到最小生成树。
全文共四章。
第1章介绍了问题背景以及相关的基础知识。
在本章中,还给出了具体的生成树的说明和克鲁斯卡尔算法。
第2章主要介绍了解决课题的算法概述以及数据结构设计。
第3章主要介绍了功能函数的实现,其中包括生成树的连式存储、结点定义以及插入排序法。
第4章是本次课程设计的总结。
全文的最后是致谢、参考文献和对程序优化处理的源代码。
XXX
2014-6-18于武汉工程大学理学院
第1章课题背景
1.1问题背景
如图1-1所示,求其最小生成树?
1.2基础知识
我们现在来考虑一个问题,即如何决定每条边都以实数值赋权的有权图的最小生成树问题。
设
是一连通的有权图,若
是
的一棵生成树,
的树枝的集合为
,则
的所有树枝的权的和
称为
的权。
若生成树
在所有生成树中有最小的权,则称
是
的最小生成树。
这个问题也具有明显的实际背景,例如,设图
中的结点表示一些城市,边表示城市之间的公路,边的权表示公路的长度。
如果用通信线路把这些城市联系起来,并且线路要求沿着道路架设,如何使得所有的线路最短呢?
这个问题实质上就是求
的最小生成树问题。
其他如水渠的布置,交通线路的规划,等等,都与这个问题有关。
下面介绍一种求最小生成树的算法。
算法(克鲁斯克尔(Kruskal)算法)
设
是一具有
个结点的连通有权图,构造
的最小生成树:
选取
中一条边
,使
在
的所有边中有最小的权。
令
,
,置
为1;
若已选好
,从
中选一条边
使其满足下列条件:
中不含有环;
在
的满足
的所有边中,
有最小的权。
若满足上述条件的边
不存在,则
就是最小生成树。
否则令
;
增加1,并返回到第
步。
1.3意义
该算法的出发点是基于“最小投资”,应用前景比较广泛。
在许多大型基础投资设施建设项目中均可运用该算法进行干路线路的初步选择。
通过学习最小生成树的有关算法思想,不仅让我们熟悉和巩固了我们已学的不牢固的知识,而且对于我们以后去解决这样类似的问题有着莫大的帮助。
1.4文献综述
文献[1]介绍了二叉树结点的形成与层次遍历。
文献[2]以实例较为详细地介绍了二叉树的分析算法与实现。
第2章基于最小生成树的算法及思想
2.1算法概述
根据课题要求,我们将通过二维数组来存储数据。
定义边结点,根据边信息创建边数组,创建边数组的输出函数。
通过读取边数组信息,对数据按权值进行升序排序,这里我们采用插入排序法,然后对于处理过的数据,根据边数组和连通描述数组来执行克如斯卡尔算法,从而得到最小生成树。
2.2数据结构设计
定义一个结构体来表示生成树的结点,结构体里包含结点顶点,结点终点串,结点之间的权值。
结点顶点、终点表示顶点与终点是否有连通性;结点之间的权值表示顶点与终点之间的距离。
将生成树存储在二维数组中,代码如下:
EdgeInfo[EdgeNum][3]={{0,1,14},{1,0,14},{0,2,12},{2,0,12},{0,3,7},{3,0,7},{0,4,10},{4,0,10},{1,2,9},{2,1,9},{1,3,13},{3,1,13},{1,4,5},{4,1,5},{2,3,6},{3,2,6},{2,4,8},{4,2,8},{3,4,11},{4,3,11}};
将边结点定义成结构体,代码如下:
structEdge
{intu,v,w;//定义边的起点,终点与权值
};
第3章功能函数实现
3.1生成树的输出函数建立
生成树的输出函数带一个参数,用来输出边结点信息。
生成树的输出函数详细设计代码如下:
voidDispEdgeArray(EdgeE[EdgeNum])
{cout<<"边数组如下"<cout<<"序号"<<"\t"<<"起点"<<"\t"<<"终点"<<"\t"<<"权值"<for(inti=0;icout<
}
3.2生成树的升序排序
插入第一个边数组信息时,不需比较,直接插入进去;插入第二个边数组信息时,要与已插入的第一个数据作比较,如果第二个数据按权值比较比第一个大,则插在第一个数据的后面,否则插在第一个数据的前面;插入第三个数据时,要与已插入的两个数据作比较,如果比两个数据都小,则插在第一个数据的前面,如果比两个数据都大,则插在第二数据的后面,如果比第一个数据大而比第二个数据小,则插在它们之间,依次类推,进行升序排序。
生成树的升序排序详细设计代码如下:
voidInsertSort(EdgeE[EdgeNum])
{inti,j;
Edgetemp;
for(i=1;i{temp=E[i];
j=i-1;
while(j>=0&&temp.w{E[j+1]=E[j];//将w大于E[i].w的纪录后移
j--;
}
E[j+1]=temp;//在j+1处插入E[i]
}
}
3.3克鲁斯卡尔算法函数
克鲁斯卡尔算法函数带有两个参数,第一个参数是为了引进边数组信息,第二个参数是为了检查各边数组的结点之间的连通性。
如果结点之间具有连通性,则不会输出结点之间的路径;反之,则会输出结点之间的路径。
克鲁斯卡尔算法函数详细设计代码如下:
voidKruskal(EdgeE[EdgeNum],intV[VexNum])
{inti,j,m1,m2,sn1,sn2,k;
k=1;//最小生成树的边数计数
j=0;//从边数组E[0]开始构建最小生成树
while(k{m1=E[j].u;m2=E[j].v;//取一条边的起点与终点
sn1=V[m1];sn2=V[m2];//检查两顶点的连通类别
if(sn1!
=sn2)//如果两顶点不属于同一连通类
{//输出该边与权值
cout<<"边("<k++;//最小生成树的边数增1
for(i=0;iif(V[i]==sn2)//使sn2类顶点归并为sn1类顶点
V[i]=sn1;
}
j++;//扫描下一条边
}
}
3.4程序与所实现的最小生成树方案
#include
#defineVexNum5//顶点数
#defineEdgeNum20//边数
#defineINF2147483647//定义无穷大
usingnamespacestd;
//给出边信息全局数组
intEdgeInfo[EdgeNum][3]={{0,1,14},{1,0,14},{0,2,12},{2,0,12},{0,3,7},
{3,0,7},{0,4,10},{4,0,10},{1,2,9},{2,1,9},{1,3,13},{3,1,13},{1,4,5},{4,1,5},
{2,3,6},{3,2,6},{2,4,8},{4,2,8},{3,4,11},{4,3,11}};
//边结点定义
structEdge
{intu,v,w;//定义边的起点,终点与权值
};
//据边信息,创建边数组
voidCreateEdgeArray(EdgeE[EdgeNum])
{for(inti=0;i{E[i].u=EdgeInfo[i][0];//边的起点
E[i].v=EdgeInfo[i][1];//边的终点
E[i].w=EdgeInfo[i][2];//边的权值
}
}
//输出边数组
voidDispEdgeArray(EdgeE[EdgeNum])
{cout<<"边数组如下"<cout<<"序号"<<"\t"<<"起点"<<"\t"<<"终点"<<"\t"<<"权值"<for(inti=0;icout<
}
//对边数组按权值作升序排序(插入排序法)
voidInsertSort(EdgeE[EdgeNum])
{inti,j;
Edgetemp;
for(i=1;i{temp=E[i];
j=i-1;
while(j>=0&&temp.w{E[j+1]=E[j];//将w大于E[i].w的纪录后移
j--;
}
E[j+1]=temp;//在j+1处插入E[i]
}
}
//据边数组E与连通描述数组V,执行克鲁斯卡尔算法
voidKruskal(EdgeE[EdgeNum],intV[VexNum])
{inti,j,m1,m2,sn1,sn2,k;
k=1;//最小生成树的边数计数
j=0;//从边数组E[0]开始构建最小生成树
while(k{m1=E[j].u;m2=E[j].v;//取一条边的起点与终点
sn1=V[m1];sn2=V[m2];//检查两顶点的连通类别
if(sn1!
=sn2)//如果两顶点不属于同一连通类
{//输出该边与权值
cout<<"边("<k++;//最小生成树的边数增1
for(i=0;iif(V[i]==sn2)//使sn2类顶点归并为sn1类顶点
V[i]=sn1;
}
j++;//扫描下一条边
}
}
//主函数
voidmain()
{EdgeE[EdgeNum];//定义一个边数组
CreateEdgeArray(E);//据边信息创建边数组
DispEdgeArray(E);//显示边数组
InsertSort(E);//对边数组按权值作升序排序
DispEdgeArray(E);
intV[VexNum];//定义一个连通描述数组
for(inti=0;iV[i]=i;//连通描述数组初始化
cout<<"克鲁斯卡尔算法求得最小生成树为:
"<Kruskal(E,V);//据E与V,执行克鲁斯卡尔算法
}
程序运行结果见图3-1
程序运行结果图3-1
3.5程序的优缺点及改进
本程序是通过二维数组方式存储数据,利用插入排序法将原始数据进行升序排序,然后将升序后的数据在克鲁斯卡尔算法函数中执行完成最小生成树的生成,最后用输出功能函数将其结果输出。
优点就是全局变量的定义,如果给出任意的一种带权值的无向图,我们只需要改变结点的顶点最大数和边数的最大数以及改变存储的数据就可以运行程序得到相应的最小生成树;缺点就是二维数组的链式存储属于静态数据结构,不能动态分配内存。
数据的升序排序可以用快速排序法来完成,这样排序迅速而简单。
第4章总结
这次程序运用到了二维数组的连式结构存储,每一个步骤都有自己应有的功能函数,条理分明,容易理解。
在这次课程设计中,仍有不足的地方。
这里,我们只解决了图论无向图的最小生成树的问题,但如果涉及到有向图,那就没办法做了。
而且通过算法只能解决无向图的连通性,而不能解决有向图的连通性。
其实,最小生成树的算法设计还可以用普里姆算法来完成。
至于边数组的升序排序,可以利用到很多方法完成。
例如,冒泡排序法、选择排序法、希尔排序法、快速排序法等等方法。
在这个程序设计中的优点就是全局变量的定义,如果给出任意的一种带权值的无向图,我们只需要改变结点的顶点最大数和边数的最大数以及改变存储的数据就可以运行程序得到相应的最小生成树。
通过学习算法与数据结构,我们了解到电脑上的许多东西都可以用算法来进行实现。
在老师的严格教导下,我们从中收获颇多。
像什么单链表操作及队列操作的学习和编程,对于它们,我们已经有了一定的了解。
以后的学习,我们应更加刻苦钻研。
平时学习不够扎实,没有刻苦的钻研,就会导致我们这次的诸多不顺。
这次的苦果是我们咎由自取,我想我们应该要更加学习了,虽然说出来比较容易,但真的要学习了。
没有一个人会喜欢自己做得很差,相反地,任何人都希望做得最好。
不过,在我的词典里,没有最好,只有更好。
伟大的科学家爱因斯坦说过,成功等于99%的努力加1%的灵感。
在得到这些荣誉的前提下,我们必须付出珍贵的时间和辛勤的汗水。
致谢
感谢我的老师的辛勤付出与授课。
没有老师孜孜不倦的教导和栽培,我认为我是学不到这宝贵的知识的。
感谢同学及朋友的关怀和帮助。
没有那些温情的暖和,我认为我的心也会沉迷于浮躁中。
大家在一起,即是缘分,感谢大家,感谢我们能相遇在一起的缘分。
无路何时,我都会倍加珍惜这份感情,再次,谢谢大家。
参考文献
[1]陈惠南.数据结构.北京:
清华大学出版社,2007.
[2]洪帆.离散数学.北京:
华中科技大学出版社.2007.
附录
#include
#defineVexNum5//顶点数
#defineEdgeNum20//边数
#defineINF2147483647//定义无穷大
usingnamespacestd;
//给出边信息全局数组
intEdgeInfo[EdgeNum][3]={{0,1,14},{1,0,14},{0,2,12},{2,0,12},{0,3,7},
{3,0,7},{0,4,10},{4,0,10},{1,2,9},{2,1,9},{1,3,13},{3,1,13},{1,4,5},{4,1,5},
{2,3,6},{3,2,6},{2,4,8},{4,2,8},{3,4,11},{4,3,11}};
//边结点定义
structEdge
{intu,v,w;//定义边的起点,终点与权值
};
//据边信息,创建边数组
voidCreateEdgeArray(EdgeE[EdgeNum])
{for(inti=0;i{E[i].u=EdgeInfo[i][0];//边的起点
E[i].v=EdgeInfo[i][1];//边的终点
E[i].w=EdgeInfo[i][2];//边的权值
}
}
//输出边数组
voidDispEdgeArray(EdgeE[EdgeNum])
{cout<<"边数组如下"<cout<<"序号"<<"\t"<<"起点"<<"\t"<<"终点"<<"\t"<<"权值"<for(inti=0;icout<
}
//对边数组按权值作升序排序(插入排序法)
voidInsertSort(EdgeE[EdgeNum])
{inti,j;
Edgetemp;
for(i=1;i{temp=E[i];
j=i-1;
while(j>=0&&temp.w{E[j+1]=E[j];//将w大于E[i].w的纪录后移
j--;
}
E[j+1]=temp;//在j+1处插入E[i]
}
}
//据边数组E与连通描述数组V,执行克鲁斯卡尔算法
voidKruskal(EdgeE[EdgeNum],intV[VexNum])
{inti,j,m1,m2,sn1,sn2,k;
k=1;//最小生成树的边数计数
j=0;//从边数组E[0]开始构建最小生成树
while(k{m1=E[j].u;m2=E[j].v;//取一条边的起点与终点
sn1=V[m1];sn2=V[m2];//检查两顶点的连通类别
if(sn1!
=sn2)//如果两顶点不属于同一连通类
{//输出该边与权值
cout<<"边("<k++;//最小生成树的边数增1
for(i=0;iif(V[i]==sn2)//使sn2类顶点归并为sn1类顶点
V[i]=sn1;
}
j++;//扫描下一条边
}
}
//主函数
voidmain()
{EdgeE[EdgeNum];//定义一个边数组
CreateEdgeArray(E);//据边信息创建边数组
DispEdgeArray(E);//显示边数组
InsertSort(E);//对边数组按权值作升序排序
DispEdgeArray(E);
intV[VexNum];//定义一个连通描述数组
for(inti=0;iV[i]=i;//连通描述数组初始化
cout<<"克鲁斯卡尔算法求得最小生成树为:
"<Kruskal(E,V);//据E与V,执行克鲁斯卡尔算法
}