一、普利姆算法
1、算法思想
从图中任选一个顶点,把它当作一棵树,然后从与这棵树相接的边中选取一条权值最小的边,并将这条边及其所连接的顶点并入这棵树中,此刻就得到了一棵有三个顶点的树。然后从与这棵树相接的边中任选一条权值最小的边,将这条边及其所连接的顶点并入这棵树中,依次类推,直到图中所有的顶点都被并入树中为止,此时所得的树就是最小生成树。
例如,图 1-1a所示的带权无向图采用普利姆算法求解最小生成树的过程如
图 1-1b~1-1e所示,以顶点0为起点,生成树过程如下:
1)如图1-1a所示,与顶点0相接的三条边的权值分别为5、1、2,最小边权值为1;
2)如图1-1b所示,选择权值为1的边,此时候选边的权值分别为5、3、2、6和2,最小边权值为2;
3)如图1-1c所示,选择权值为2的边,此时候选边的权值分别为5、3、2和3,最小边权值为2;
4)如图1-1d所示,选择权值为2的边,此时候选边的权值分别为5、3和4,最小边权值为3;
5)如图1-1e所示,选择权值为3的边,此时所有的顶点都已并入生成树中,最小生成树求解过程完毕。
2、普利姆算法执行过程
用普利姆算法构造最小生成树的过程中,需要建立两个数组vist[]和lowcost[]。当vist[i] = 1时,表示顶点i已经被并入生成树中,vist[i] = 0表示顶点i还未被并入生成树中。lostcost[]数组中存放当前生成树到剩余顶点最短边的权值。
执行过程如下:
1)将顶点v0到其他顶点的所有边作为侯选边。
2)重复以下步骤n - 1次,使得其他n - 1个顶点被并入到生成树中。
I 从侯选边中挑选权值最小的边输出,并将与该边另一端相接的顶点v并入到生成树中;
II 查看所有剩余顶点vi,如果(v,vi)的权值比lowcost[vi]小,则用
(v,vi)的权值更新lowcost[vi]。
void Prim(MG g ,int v0,int &sum)
{
int lowcost[maxSize], vist[maxSize], v;
int i , j , k , min;
v = v0;
for(i = 0;i < g.n;i++){
lowcost[i] = g.edges[v0][i];
vist[i] = 0;
}
vist[v0] = 1;
sum = 0;
for(i = 0;i<g.n - 1;++i){
min = INF;
for(j = 0 ;j<g.n; ++j){
if(vist[j] == 0&&lowcost[j]<min){
min = lowcost[j];
k = j;
}
}
vist[k] = 1;
v = k;
sum += min;
for(j = 0 ; j<g.n;++j){
if(vist[j] ==0 &&g.edges[v][j]<lowcost[j]){
lowcost[j] = g.edges[v][j];
}
}
}
}
二、克鲁斯卡尔算法
1、算法思想
每次找出侯选边中权值最小的边,就把该边并入生成树中,重复此过程,直到所有边都被检测完为止。
2、执行过程
typedef struct
{
int a,b;
int w;
}Road;
Road road[maxSize];
int v[maxSize];
int getRoot(int a ){
while(a != v[a] )
a = v[a];
return a;
}
void Kruskal(MG g,int &sum,Road road[]){
int i;
int N,E,a,b;
N = g.n;
E = g.e;
sum = 0;
for(i = 0 ; i<N;++i)
v[i] = i;
sort(road,E);
for(i = 0;i<E;++i){
a = getRoot(road[i].a);
b = getRoot(road[i].b);
if(a != b){
v[a] = b;
sum += road[i].w;
}
}
}
三、两种算法的比较
1、普利姆算法时间复杂度为O(n^2),普利姆算法的时间复杂度只与图中顶点有关系,与边数没有关系,因此普利姆算法适用于稠密图; 克鲁斯卡尔算法时间主要花费在sort()函数上,因此克鲁斯卡尔算法的时间复杂度主要由选取的排序算法决定,排序算法所处理数据的规模由图的边数决定,与顶点数无关,因此克鲁斯卡尔算法适用于稀疏图;
2、普利姆算法和克鲁斯卡尔算法都是针对于无向图。