您的位置:首页 > 其它

Prim 算法求最小生成树 O(n^2)

2016-04-09 19:19 281 查看
最小生成树: 给出一个无向图 G=(V ,E) , V(vertex)表示 图上点的集合, E(edge)表示这个图上边的集合。对于图上每一条边(u,v)∈ E , 都有一个权值 w(u,v)。我们希望找出一个不含有回路的自己T⊆ E,它连接了所有的节点。(通俗的说:就是在一个无向图上选出一些边,是所有点连同,并且无环(因为生成的结果是一个树
,树是无环的))。而最小生成树是 是取出的所有边的权重之和最小的那个树。 也就是∑wi的和最小。

在一个具有几个顶点的连通图G中,如果存在子图G'包含G中所有顶点和一部分边,且不形成回路,则称G'为图G的生成树代价最小生成树则称为最小生成树。

 许多应用问题都是一个求无向连通图的最小生成树问题。例如:要在n个城市之间铺设光缆,主要目标是要使这 n 个城市的任意两个之间都可以通信,但铺设光缆的费用很高,且各个城市之间铺设光缆的费用不同;另一个目标是要使铺设光缆的总费用最低。这就需要找到带权的最小生成树。

最小生成树 常用的有两种 贪心算法: kruskal 算法和 prim 算法。

Prim 算法是一种贪心算法(greedy algorithm)。

从单一顶点开始,普里姆算法按照以下步骤逐步扩大树中所含顶点的数目,直到遍及连通图的所有顶点。

输入:一个加权连通图,其中顶点集合为V,边集合为E;
初始化:Vnew = {x},其中x为集合V中的任一节点(起始点),Enew = {};
重复下列操作,直到Vnew = V:

在集合E中选取权值最小的边(u, v),其中u为集合Vnew中的元素,而v则不是(如果存在有多条满足前述条件即具有相同权值的边,则可任意选取其中之一);
将v加入集合Vnew中,将(u, v)加入集合Enew中;

输出:使用集合Vnew和Enew来描述所得到的最小生成树。

给定一个root 找出距离root 最近的点 V,也就是边上的权值最小,在找出据 root 和V 的点中距离最近的点。

也就是 有两个几何 S 和V -S 。S指所有已选出的点S,V-S 指待选出的点。

每次 把V-S这个集合中每个点 距离 V每个点都有一个距离,选出距离最小的点。







(引用wiki 上的关于prim 算法的图片 http://zh.wikipedia.org/wiki/%E6%99%AE%E6%9E%97%E6%BC%94%E7%AE%97%E6%B3%95

证明(自己画的不好,见谅):


假如此图为最小生成树的T。

在选取时:黄的点为当前的S,白色的点是即将要选的点V-S。。

反证: 假设 当 V 是距离S中的点,即距离黄色的点最小的,也就是 w(u,v)的值最小。

但我们不选而选了另一条边 (X,Y),w(X,Y)>w(u,v)。

u,v在最小生成树中定是联通的。。

当选(X,Y)时 : 最小权值的和 为A = Sum(w)。

而当选(u,v)而不选(X,Y)时就是另一棵树T*:最小权值的和 B = Sum(w)-w(X,Y)+w(u,v);

因为 w(X,Y)>w(u,v) ;则 B<A 这就与T是最小生成树矛盾,而T* 才是最小生成树。

代码:
int prim(int st) {
for (int i = 1; i <= n; i++) {
dis[i] = inf;
vst[i] = false;
}
int sum = 0;   dis[st] = 0;
for (int i = 1; i <=n; i++) {
int Min = inf, k;
for (int j = 1; j <= n; j++)
if (!vst[j] && dis[j] < Min) {
Min = dis[j];
k = j;
}
sum += Min;
vst[k] = true;
for (int j = 1; j <= n; j++)
if (!vst[j] && adj[k][j] < dis[j]) {
dis[j] = adj[k][j];
}
}
return sum;
}


注释: adj[k][j] 是图G的邻接矩阵。vst[i] 标记是否visit ,也就是是否已经选出了。

dis[i] 记录每个没选的点到所有已选的点的最小距离。

当root 选出时,把dis[ j ] 初始化为 adj[root][ j ];

但新的点 a选出时,只要更新 dis[ j ] < adj[a][ j ] 的 j ,dis[ j ] = adj[a][ j ];

这时dis[ j ]还是记录 每个点j 到已选出的点最小值。因为我们从第一次赋值开始到最后一次更新dis[ j ],这个过程就枚举了未选的点 j 到以选出点的距离,也就是权值。当选出一个点后,立即更新 dis[ j ] ,当 dis[ j ] < adj[a][ j ] 时,dis[ j ] = adj[a][ j ];
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: