Prim算法:
图论中的一种算法,可在加权连通图里搜索最小生成树。意即由此算法搜索到的边子集所构成的树中,不但包括了连通图里的所有顶点,且其所有边的权值之和亦为最小。
Prim算法的描述:
已知带权连通图G(V,E),求其最小生成树T(Vnew,Enew),步 骤如下:
1.初始从G中任选一顶点v0,Vnew={v0},Enew={};
2.从V-Vnew中选取一顶点v,使得v到树T的距离最小,将v加入Vnew中,对应的边加入Enew中
3.重复步骤2,直到Vnew=V;
例
求下图的最小生成树
此为原始的加权连通图。每条边一侧的数字代表其权值。
顶点D被任意选为起始点。顶点A、B、E和F通过单条边与D相连。A是距离D最近的顶点,因此将A及对应边AD以高亮表示。Vnew={D,A},Enew={(D,A)};
下一个顶点为距离D或A最近的顶点。B距D为9,距A为7,E为15,F为6。因此,F距D或A最近,因此将顶点F与相应边DF以高亮表示。Vnew={D,A,F} Enew={(D,A),(D,F)}
算法继续重复上面的步骤。距离A为7的顶点B被高亮表示。
Vnew={D,A,F,B} Enew={(D,A),(D,F),(A,B)}
在当前情况下,可以在C、E与G间进行选择。C距B为8,E距B为7,G距F为11。点E最近,因此将顶点E与相应边BE高亮表示。Vnew={D,A,F,B,E} Enew={(D,A),(D,F),(A,B),(B,E)}
这里,可供选择的顶点只有C和G。C距E为5,G距E为9,故选取C,并与边EC一同高亮表示。Vnew={D,A,F,B,E,C} Enew={(D,A),(D,F),(A,B),(B,E),(E,C)}
顶点G是唯一剩下的顶点,它距F为11,距E为9,E最近,故高亮表示G及相应边EG。Vnew={D,A,F,B,E,C,G} Enew={(D,A),(D,F),(A,B),(B,E),(E,C),(E,G)}
所有顶点均已被选取,图中绿色部分即为连通图的最小生成树。在此例中,最小生成树的权值之和为39。
Prim算法的代码
#include<stdio.h>
#include<string.h>
#define MaxSize 50
#define INF 10000000
double graph[MaxSize][MaxSize];
double prim(int n) //初始选取顶点0
{
double lowcost[n],sum = 0; //数组lowcost用来存储V-T中的顶点到T的最短距离,lowcost[i] = 0表示顶点i在树T中
int count = 1,vetrix[n]; //vetrix[i]表示i到T中顶点vetrix[i]的距离最小,用于打印路径
lowcost[0] = 0;
for(int i = 1;i < n;i++)
{
lowcost[i] = graph[0][i];
vetrix[i] = 0;
}
while(count < n)
{
double min = INF;
int min_index;
for(int i = 1;i < n;i++)
{
if(lowcost[i] != 0 && lowcost[i] < min) //找到权最小的边<vetrix[i],i>,其中vetrix[i]在T中,i在V-T中
{
min = lowcost[i]; //min记录最小的权值
min_index = i; //min_index记录最小边对应的顶点,即要加入T中的顶点
}
}
sum += min;
lowcost[min_index] = 0;
count++;
printf("%d->%d\n", vetrix[min_index],min_index);
for(int i = 1;i < n;i++)
{
if(lowcost[i] != 0 && lowcost[i] > graph[min_index][i]) //由于新加入T的顶点min_index,更新V-T中的顶点到T的最短距离,及对应的顶点
lowcost[i] = graph[min_index][i];
vetrix[i] = min_index;
}
}
return sum;
}
int main()
{
int n, m, v1, v2;
double w;
scanf("%d %d",&n,&m);
for(int i = 0;i < n;i++)
{
for(int j = 0;j < n;j++)
{
graph[i][j] = INF;
}
}
for(int i = 0;i < m;i++)
{
scanf("%d %d %lf",&v1, %v2, &w);
graph[v1][v2] = graph[v2][v1] = w;
}
printf("%lf\n",prim(n));
return 0;
}
代码测试
输入:
10 18
0 1 12.7
0 8 12.4
0 7 13.4
1 2 12.6
1 8 12.1
2 3 12.9
2 4 24.2
2 8 23.6
2 9 23.3
3 4 22.2
3 5 22.1
3 6 15.3
4 5 14.4
5 9 12.5
6 7 21.7
6 9 13.2
7 8 21.8
8 9 12.8
输出:
0->8
8->1
1->2
8->9
9->5
2->3
9->6
0->7
5->4
116.300000
Prim算法的时间复杂度: O(n^2)
与图的顶点数目有关,另一种算法Krusal与边的数目有关;