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

来源:互联网 发布:c语言可视化界面设计 编辑:程序博客网 时间:2024/06/06 03:40

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

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

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

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

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

 

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

  1. 输入:一个加权连通图,其中顶点集合为V,边集合为E;
  2. 初始化:Vnew = {x},其中x为集合V中的任一节点(起始点),Enew = {};
  3. 重复下列操作,直到Vnew = V:
    1. 在集合E中选取权值最小的边(u, v),其中u为集合Vnew中的元素,而v则不是(如果存在有多条满足前述条件即具有相同权值的边,则可任意选取其中之一);
    2. 将v加入集合Vnew中,将(u, v)加入集合Enew中;
  4. 输出:使用集合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 ];


   

原创粉丝点击