最小代价生成树
一:Prim算法
(一):算法思想
从图中任取一顶点,将其作为一颗树,然后从与这棵树相接的边中选取一条最短的边(权值最小的边),并将这条边及其所相连的顶点也并入到这棵树中,此时得到了一颗有两个顶点的树。然后从与这棵树相接的边中选取一条最短的边,并将这条表及其所连顶点并入到当前树中,得到一棵有3个顶点的树。以此类推,直到树中的所有顶点都被并入到树中为止。此时得到的生成树就是最小生成树。
(二):算法执行过程
从树中的某一个顶点v开始。构造生成树的算法执行如下:
1、 将v到其他顶点的所有边当作候选边
2、重复以下步骤n-1次,使得其他n-1个顶点被并入到生成树中
- 从候选边中挑选出权值最小的边输出,并将与该边另一端相接的顶点v并入到生成树中
- 考察所有剩余顶点vi,如果(v,vi)的权值比lowcost[vi]小,则用(v,vi)的权值更新lowcost[vi].
使用prim算法生成最小生成树的过程中,需要建立两个数组vset[]和lowcost[]。
vset[i]=1表示顶点i已经被并入到生成树中,vset[i]=0表示顶点i还没有被并入到生成树中。
lowcost[]数组中存放当前生成树到剩余各顶点最短边的权值。
(三):代码
typedef struct{
int edge[20][20];
int vexs[20];
int arcnum; //边的数量
int vexnum; //顶点的数量
}MGraph;
//Prim算法
/**
*
* @param G 使用邻接矩阵存储的图
* @param sum 路径长度
* @param v 开始顶点
*/
void Prim(MGraph G,int *sum,int v){
int lowcost[20];
int vset[20];
int min;
int k;
//初始化相关条件
for (int i = 0; i < G.vexnum; ++i) {
lowcost[i] = G.edge[v][i];
vset[i] = 0;
}
vset[v] = 1;
//下面是关键操作
*sum = 0;
for (int i = 0; i < G.vexnum-1; ++i) {
min=10000; //10000表示的是图中所有数据中最大
for (int j = 0; j < G.vexnum; ++j) {
if (vset[j]==0&&lowcost[j]<min){ //选出当前生成树到其余各顶点最短边中的最短的一条
min = lowcost[j];
k = j;
}
}
vset[k] = 1; //表示已经将k顶点加入到树中
*sum+=min;
for (int j = 0; j < G.vexnum; ++j) { //该循环是以刚并入到生成树中的顶点k为媒介,来更新侯选边
if (vset[j]==0&&G.edge[k][j]<lowcost[j]){
lowcost[j] = G.edge[k][j];
}
}
}
}
二:克鲁斯卡尔算法
(一);算法思想
每次找出侯选边中权值最小的边,就将该边并入到生成树中,重复此过程直到所有的边都检测完为止。
(二):算法执行过程
将图中的边的权值按照大小进行排序,然后从最小的便开始扫描各边,并检测当前边是否为侯选边,即是否将该边加入之后会构成回路,如不构成回路,则将该边并入到当前的生成树中,直到所有的边都被检测完之后。
本博客写的克鲁斯卡尔算法使用了并查集来判断是否会产生回路,如果同学们对于并查集不熟悉,请查看这篇博客:Kruskal算法
(三):代码
typedef struct {
int a,b; //两个顶点
int w; //权重
}Road;
Road road[1000];
int v[1000]; //定义并查集
int getRoot(int a){ //在并查集中查找根节点使用的函数
while (a!=v[a]){
a = v[a];
}
return a;
}
void Kruskal(MGraph G,int *sum,Road road[]){
*sum = 0;
int a,b;
for (int i = 0; i < G.vexnum; ++i) {
v[i] = i;
}
sort(road,G.arcnum); //对road数组中的边进行从小到大进行排序
for (int i = 0; i < G.arcnum; ++i) {
a = getRoot(road[i].a);
b = getRoot(road[i].b);
if (a!=b){
v[a] = b;
*sum = *sum+road[i].w; //求生成树的权值,
}
}
}