Prim算法是由R.C.Prim于1956年提出,是一种构造性算法。假设G=(V,E)是一个具有n个顶点的带权无向连通图,T=(U,TE)是G的最小生成树,其中,U是T的顶点集,TE是T的边集,则由G构造从起始顶点v出发的最小生成树T的步骤如下:
- 初始化U={v},以v到其他顶点的所有边为候选边;
- 从候选边中挑选出权值最小的边加入TE,设该边在V-U中顶点为k,将k加入U中;
- 考察当前V-U中所有的顶点j,修改候选边,如果(k,j)的权值小于原来和顶点j关联的候选边,则用(k,j)取代后者作为候选边;
- 重复2~3两步n-1次,就能得到最小生成树T;
Prim算法过程图解:
v表示顶点,dist存放候选边,标记为红色表示属于最小生成树中的顶点和边:
(1)选0作为初始顶点添加到最小生成树的点集中,以0到其他顶点的边作为候选边,dist[v]=∞表示当前不存在到顶点v的边;
(2)从候选边中选出权值最小为1的边添加到最小生成树的边集中,以该边所对应的顶点2到其顶点的边更新数组dist,顶点2添加到最小生成树的点集中;
(3)从候选边中选出权值最小为4的边添加到最小生成树的边集中,以该边所对应的顶点5到其顶点的边更新数组dist,顶点5添加到最小生成树的点集中;
(4)从候选边中选出权值最小为2的边添加到最小生成树的边集中,以该边所对应的顶点3到其顶点的边更新数组dist,顶点3添加到最小生成树的点集中;
(5)从候选边中选出权值最小为5的边添加到最小生成树的边集中,以该边所对应的顶点1到其顶点的边更新数组dist,顶点1添加到最小生成树的点集中;
(6)从候选边中选出权值最小为3的边添加到最小生成树的边集中,以该边所对应的顶点4到其顶点的边更新数组dist,顶点4添加到最小生成树的点集中;
至此就得到一颗最小生成树;
模板函数:
const int Maxsize = 105;
const int Inf=0x3f3f3f3f;
int map[Maxsize][Maxsize];//邻接矩阵存图
int dist[Maxsize]; //最小生成树的候选边集
bool vis[Maxsize]={0}; //1表示最小生成树的点
int n;
int prim(int v)//初始顶点v
{
int ans=0; //最小生成树上所有边的权值和
for(int i=0;i<n;i++) //通过初始顶点v到其他顶点的边更新候选边集dist
dist[i]=map[v][i];
vis[v]=1; //标记为1,表示添加到最小生成树的点集中
while(1) //最多n-1次
{
int Min=Inf; //存放候选边最小权值,初始化为最大
v=-1; //存放最小权值候选边所对应的顶点
for(int i=0;i<n;i++) //遍历候选边集
{
if(!vis[i]&&dist[i]<Min)//如果顶点i不在最小生成树点集中,且到i的候选边权值小于Min
{
Min=dist[i]; //更新最小权值
v=i; //更新小权值候选边所对应的顶点
}
}
if(v==-1) //没找到符合条件的顶点就结束循环
break;
vis[v]=1; //将顶点v添加到最小生成树的点集中
ans+=dist[v]; //累加最小生成树上的边权值
for(int i=0;i<n;i++) //通过v到其他顶点的边更新候选边集dist
if(!vis[i]&&map[v][i]<dist[i])//顶点i没被标记,且(v,i)的权值小于dist[i]
dist[i]=map[v][i]; //更新候选边集
}
return ans; //放回最小生成树的权值和
}