一个无向图的最小生成树由该无向图的那些连接相互连接的顶点的边构成的树,且使得该树的所有边的总权值和最小。最小生成树问题在实际生活中也广泛存在,例如:要在一个村庄修建一条公路,连通到村里的每户人家,选择怎么的路线铺设道路使得总的距离最短(造价最低)。
最小生成树算法之一:Prim算法,将整个顶点集合分为两个子集U、V,U中存放已经在生成树中的顶点,V中存放未在生成树中的顶点。算法核心的每一步将从U、V中各选一顶点,使得边的权值w(u,v)最小,然后将该顶点v从V中移到U中,如此直到集合V为空,即完成。该过程如下图所示:
Prim算法的基本步骤
在此以求解如下无向图的最小生成树为例,给出朴素Prim算法的实现代码:
C代码:
<span style="font-size:18px;">#include <stdio.h>
#include <stdlib.h>
#define Max 100
#define MAXCOST 0x7fffffff
int graph[Max][Max];//存放图中任意两顶点对应的边值,边值为MAXCONST即表示两顶点不相通
int Prim(int graph[][Max],int n)
{
int lowcost[Max];//记录到终点i对应的边值,lowcost[i]=0表示点i已经在生成树中
int start[Max];//记录终点i对应的起点,start[i]=0表示点i已经在最小生成树中
int i,j,min,minid,sum;
min=0;
minid=0;
//默认开始时将一号顶点加入生成树中,因此从二号顶点开始初始化。共n个顶点,生成树需要n-1条边
for(j=2;j<=n;++j)
{
lowcost[j]=graph[1][j];
start[j]=1;
}
lowcost[1]=0;//默认将一号顶点加入生成树
sum=0;
for(i=2;i<=n;++i)
{
min=MAXCOST;
for(j=2;j<=n;++j)//查找满足条件(即起点在生成树中,终点在生成树外)的最小边值
{
if(lowcost[j]<min && lowcost[j]!=0)
{
min=lowcost[j];
minid=j;
}
}
//输出生成树中的该边
printf("%c to %c: %d \n",start[minid]+'A'-1,minid+'A'-1,lowcost[minid]);
sum+=lowcost[minid];
lowcost[minid]=0;//将该点加入生成树中
for(j=2;j<=n;++j)
{
if(graph[minid][j]<lowcost[j])//发现更小的权值(注:这句代码同时保证了点j不在生成树中且与点minid连通)
{
lowcost[j]=graph[minid][j]; //更新边值权值
start[j]=minid; //更新边的起点
}
}
}
return sum;//返回最小生成树的边值之和
}
/********************************/
/*测试
输入数据:
7 11
A B 7
A D 5
B C 8
B D 9
B E 7
C E 5
D E 15
D F 6
E F 8
E G 9
F G 11
*/
int main()
{
int i, j, k, v, e;
int x, y, cost;
char chx, chy;
//读取节点和边的数目
scanf("%d%d", &v, &e);
getchar();
// 初始化图,所有节点间距离为无穷大
for (i = 1; i <= v; i++)
{
for (j = 1; j <= v; j++)
{
graph[i][j] = MAXCOST;
}
}
// 读取边信息
for (k = 0; k < e; k++)
{
scanf("%c %c %d", &chx, &chy, &cost);
getchar();
i = chx - 'A' + 1;
j = chy - 'A' + 1;
graph[i][j] = cost;
graph[j][i] = cost;
}
//Prim算法
cost=Prim(graph,v);
// 输出最小权值和
printf("Total:%d\n", cost);
return 0;
}</span>
测试输出: