01基于Prim算法的最小生成树
1. 问题
给定一无向连通图G = (V, E) ,其中V为G的顶点集,E为G的边集,(u, v) 代表连接顶点 u 与顶点 v 的边,w(u, v) 代表此边的权重。
若存在树 T ,其顶点集V’与G的顶点集V一一对应、边集E’为G边集E 的子集,且T无循环,使得 w(T) 最小,则称 T 为 G 的最小生成树(spanning tree)。
如何快速、准确地找到最小生成出树呢?
2. 解析
本文主要描述基于Prim算法的最小生成树构造过程。
Prim算法的核心思想为:
1).输入:一个加权连通图,其中顶点集合为V,边集合为E;
2).初始化:V’ = {x},其中x为集合V中的任一节点(起始点),E’ = {},为空;
3).重复下列操作,直到V’ = V: a.在集合E中选取权值最小的边<u, v>,其中u为集合V’中的元素,而v不在V’集合当中,并且v∈V(如果存在有多条满足前述条件即具有相同权值的边,则可任意选取其中之一); b.将v加入集合V’中,将<u, v>边加入集合E’中;
4).输出:使用集合V’和E’来描述所得到的最小生成树。
(参考百度百科-Prim算法)
举个栗子:
给定一有6顶点的无向图连通图G = (V, E) ,顶点和边的对应关系及各边权重如下图所示:
这里首先选取 顶点C 加入最小生成树 T 的顶点集 V’ 。
其次,以已加入V’的顶点为起点,以未加入V’且在V里的顶点为终点,在边集E中选择权重最小的边,加入到E’,并判断当前的T是否构成环。
若不构成环,将终点顶点和该边分别加入到V’和E’;若构成环,不断比较出次小的边直至不成环,并将对应的终点顶点和该边分别加入到V’和E’。(图中绿线表示同一阶段进行比较的边,红线表示已加入V’或E’的点或边,蓝线表示被初步选择的边成环,该边将被舍弃。)
重复上述步骤,当V’=V时,所呈现的边集和点集即构成给定图的最小生成树。(基于此,后面步骤不再详述。)
按步骤,该阶段被选择的应该是权值为9的边,但此边的加入使得T构成了环,因此被舍弃,下同。
至此,G中所有顶点都已加入T中,最小生成树构造完毕,其权值为40.
3.设计
int Prim(SGraph g, int s) {
for ( i from 0 to MAXSIZE-1 )
//n为顶点数,初始化每个点的最小边权值为某一极大值(如65535)
//将传入的顶点s作为第一个初始顶点
node[s].data = s;
//顶点s的lowestvalue置0,表示顶点s入生成树
node[s].lowestvalue = 0;
//输入点s到其他点的边权值
for ( i from 0 to MAXSIZE-1 ) {
if (i != s) {
node[i].lowestvalue = g.A[s * MAXSIZE + i];
node[i].data = s;
}
}
//每一次往生成树加一条边,直至共加入MAXSIZE-1条边
for ( j from 1 to MAXSIZE-1 ) {
//找到最小权值边且终点顶点未被加入的顶点,记下k;
int k = Getmin(node);
//累加权值
sumValue += node[k].lowestvalue;
//顶点k的lowestvalue置0,表示顶点k入生成树
node[k].lowestvalue = 0;
//更新以未加入顶点为终点的较小边权值
for (int m = 0; m < MAXSIZE; m++) {
if (node[m].lowestvalue > g.A[k * MAXSIZE + m]) {
node[m].lowestvalue = g.A[k * MAXSIZE + m];
node[m].data = k;
}
}
}
//返回最小生成树总权值
}
4.源码
https://github.com/KabgRs/F-SDDR/tree/%E7%AE%97%E6%B3%95%E5%88%86%E6%9E%90%E4%B8%8E%E8%AE%BE%E8%AE%A1%E4%BD%9C%E4%B8%9A/01%E6%9C%80%E5%B0%8F%E7%94%9F%E6%88%90%E6%A0%91_Prim%E7%AE%97%E6%B3%95