由于数据结构学过一段时间了,有些知识点有点遗忘,所以先来复习一下一些知识点:
什么是最小生成树?
最小生成树是在一个给定的无向图G中求一棵树T,使得这棵树拥有图G中的所有顶点,且所有边都是来自图G中的边,并且满足整棵树的边权之和最小。
- 最小生成树的三个性质:
- 1,最小生成树是树,因此其边数等于顶点数减1,且树内一定不会有环。
- 2,对于给定的图G,其最小生成树可以不唯一,但其边权之和一定是唯一的
- 3,由于最小生成树是在无向图上生成的,因此其根结点可以实这棵树上的任意一个结点。
求最小生成树一般有两种算法,即prim和kruskal。这两种算法都是采用了贪心的思想,只是贪心的策略不太一样。
Prim算法:(普里姆算法)
用来解决最小生成树问题,其基本思想是对图G(V,E)设置集合S,存放已被访问的顶点,然后每次从集合V-S中选择与集合S的最短距离最小的一个顶点(记为
u),访问并加入集合S。之后,令顶点u为中介点,优化所有从u能到达的顶点v
与集合S之间的最短距离。这样的操作执行n次(n为顶点个数),直到集合S已包含所有的顶点。可以发现,prim算法的思想与最短路径中的Dijkstra算法的思想几乎完全相同,只是在涉及最短距离时使用了集合S代替Dijkstra算法中的起点s。
Kruskal算法:(克鲁斯卡尔算法)
同样是用来解决最小生成树问题的一个算法。和prim算法不同,kruskal算法采用了边贪心的策略。
基本思想:在初始状态时隐去图中的所有边,这样图中的每个顶点都自成一个连通块。
步骤:1.对所有边按边权从小到大进行排序。
2.按边权从小到大测试所有边,如果当前测试边所连接的两个顶点在不同一个连通块中,则把这条测试边加入当前最凶啊生成树中;否则,将边舍弃。
3.执行步骤2直到最小生成树中的边数等于总顶点数减一或者是测试完所有的边时结束。而当结束时如果最小生成树的边数小于总顶点数减一,说明该图不连通。
Prim算法的代码实现:
采用邻接矩阵的方式存储无向图G,自定义G的顶点数和边数。
#include<iostream>
#include<string.h>
#include<algorithm>
using namespace std;
#define INF 10000
int main() {
int numVertex;
cin>>numVertex; //输入顶点个数
int map[numVertex][numVertex];
//{
// INF, 2, 4, 1, INF, INF, INF,
// 2, INF, INF, 3, 10, INF, INF,
// 4, INF, INF, 2, INF, INF, INF,
// 1, 3, 2, INF, 7, 8, 4,
// INF, 10, INF, 7, INF, INF, 6,
// INF, INF, 5, 8, INF, INF, 1,
// INF, INF, INF, 4, 6, 1, INF
//};
fill(map[0],map[0]+numVertex*numVertex,INF); //初始化图map
int distance[numVertex]; //顶点与集合的最短距离
fill(distance,distance+numVertex,INF); //将整个distance数组赋为INF
int path[numVertex];
memset(path,0,sizeof(path));
int known[numVertex]; //标记数组known[i]=1表示已访问过,初值为false
memset(known,0,sizeof(known));
int numline,r,l,value; //输入numline 边数,r 行,l 列,value 权值。
cin>>numline;
while(numline>=1){
cin>>r>>l>>value;
map[r][l]=value;
map[l][r]=value;
numline--;
}
//默认0号为初始点,输出最小生成树的边权之和
int begin=0,ans=0;
distance[begin]=0; //只有0号顶点到集合的距离为0,其余全为INF
for(;;){
int min=INF, pos=0; //pos使得distance[pos]最小,MIN存放该最小的distance[pos]
for(int i=0;i<numVertex;i++){
if(known[i]==0 && min>=distance[i]){
min=distance[i];
pos=i;
}
} //寻找未访问的顶点中distance[]最小的,即寻找最小距离
if(pos==0) break; //找不到小于INF的distance[pos],则剩下的顶点和集合不连通
known[pos]=1; //标记pos为已访问
if(min==INF) break;
ans+=distance[pos]; //将集合距离最小的边加入最小生成树
for(int i=0;i<numVertex;i++){
if(map[pos][i]!=INF && known[i]==0){ // pos能到达的顶点并且该顶点并未被访问过
if(distance[i]>map[pos][i]){ //以pos为中介点可以使顶点离集合更近
distance[i]=map[pos][i]; //将map[][]的值赋给distance[]
path[i]=pos+1;
}
}
} //更新距离
}
cout<<"最小生成树的权值之和为:"<<ans<<endl;
return 0;
}
算法内容介绍引用来自《算法笔记》----胡凡
来源:CSDN
作者:一个奔跑的C
链接:https://blog.csdn.net/henu1710252658/article/details/104104824