普里姆算法(Prim算法),图论中的一种算法,可在加权连通图里搜索最小生成树。意即由此算法搜索到的边子集所构成的树中,不但包括了连通图里的所有顶点,且其所有边的权值之和亦为最小。
借用维基百科的定义就是:
从单一顶点开始,普里姆算法按照以下步骤逐步扩大树中所含顶点的数目,直到遍及连通图的所有顶点。
输入:一个加权连通图,其中顶点集合为V,边集合为E;
初始化:Vnew = {x},其中x为集合V中的任一节点(起始点),Enew = {};
重复下列操作,直到Vnew = V:
在集合E中选取权值最小的边(u, v),其中u为集合Vnew中的元素,而v则是V中没有加入Vnew的顶点(如果存在有多条满足前述条件即具有相同权值的边,则可任意选取其中之一);
将v加入集合Vnew中,将(u, v)加入集合Enew中;
输出:使用集合Vnew和Enew来描述所得到的最小生成树。
其实算法的基本思想就是一个贪心的概念,选取一个结点当做源结点进行扩展,扩展方法就是选取源结点与其他结点之间权值最小的那个结点,因为如果构造一个最小生成树一定会包含源节点,包含源节点那么就一定会选取距离源结点最近的那条边包含进来.之后选取一个结点之后就构成了一个最小生成树集合,将所有结点分成了两部分,每次循环依次最小生成树集合都会增加一个结点,所以只需循环verNum-1次就可以得到最终结果.
其中要不断维护的就是一个最小边的数组lowcost,也就是从集合出发到其他结点的最小边长,以便筛选结点时使用,已经选进集合的结点i的lowcost[i]将被赋值为0.每次循环中选取min的过程其实可以使用优先队列来实现,自动排序,每次只需出队就可以了.
typedef struct {
int arc[MAXVEX][MAXVEX];
int numVertexes;
}MGraph;
void prim(MGraph G)
{
int i,j;//循环变量
int min;//存放每次循环得出的边最小值
int k;//记录下一次要扩展的结点
int adjvex[MAXVEX];//存放全职最小的边的另一个结点,如adjvex[2] = 4;连接最小值边的为结点2到结点4.
int lowcost[MAXVEX];//存放最小生成树集合中结点到每个结点的最小权值.
lowcost[0] = 0;//将最小权值赋值为0说明该结点已经在最小生成树集合中
adjvex[0] = 0;//结点0到结点0的权值最小,所以adjvex[0] = 0.
for (i = 0; i < G.numVertexes; i++)//初始化,结点0所有的边值赋值给最小全集数组
{
lowcost[i] = G.arc[0][i];
adjvex[i] = 0;
}
for (i = 1; i < G.numVertexes; i++)//从1结点开始循环遍历,每循环依次往最小生成树集合中增加一个结点
{
min = INFINITY;
j = 1;
k = 0;
while(j < G.numVertexes)//循环查找集合向外扩展最小的边
{
if (lowcost[j]!=0&&lowcost[j]<min)//lowcost=0代表结点已经在集合中
{
min = lowcost[j];//记录最小边的值
k = j;//记录下一个要扩展的结点
}
j++;
}
cout << "(" << adjvex[k] << ", " << k << ")" << " "; // 打印当前顶点边中权值最小的边
lowcost[k] = 0;// 将当前顶点的权值设置为0,表示此顶点已经完成任务
for(j = 1; j < G.numVertexes; j++)//扩展集合中的边,更新lowcost数组.
{
if(lowcost[j]!=0&&G.arc[k][j] < lowcost[j])
{
lowcost[j] = G.arc[k][j];
adjvex[j] = k;
}
}
}
}