最小生成树

简介

        在一给定的无向图G = (V, E) 中,(u, v) 表明链接顶点 u 与顶点 v 的边(即),而 w(u, v) 表明此边的权重,若存在 T 为 E 的子集(即)且为无循环图,使得 w(T) 最小,则此 T 为 G 的最小生成树。最小生成树实际上是最小权重生成树的简称。html


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

构建思路

       在每一次循环迭代前,A是某个最小生成树的一个子集.在算法的每一步肯定一条边(u,v),使得其加入集合A后,集合A任然保持为一棵最小生成树,而每次加入的边(u,v)称为安全边(便是加入后不破坏循环不变式的边)ios

GENERIC-MST(G, w)
   A = 空集
   while A does not form a spanning tree
         find an edge (u,v) that is safe for A
         add (u, v) into A                   
return A

由上可见,寻找安全边是算法的关键,下面对最小生成树的部分性质进行证实。从而解决该问题.算法

最小生成树性质


算法实现

Kruskal(克鲁斯卡尔)算法

概述

      Kruskal算法是一种用来寻找最小生成树的算法,由Joseph Kruskal在1956年发表。用来解决一样问题的还有Prim算法和Boruvka算法等。三种算法都是贪婪算法的应用。和Boruvka算法不一样的地方是,Kruskal算法在图中存在相同权值的边时也有效。shell

伪代码

下面代码是以算法导论中的kruskal的伪代码改写而来的,整体思路为:
1.按照权值由低到高的顺序寻找安全边,并将其加入最小生成树中.
2.在安全边的选择上还需注意当发现该边的端点都在已构建完成的最小生成树中时代表若是加入该边将造成环,所以这种状况认为该边不安全.(简而言之安全边就是不破环循环不变式的边)
4.该算法的成立依赖于前面证实的最小生成树的性质.

MST-KRUSKAL.G(G, w)
tree = 空
sort the edges of G.E into nondecreasing order by weight w
foreach  edge(u,v)  in G.E, taken in nondecreasing order by weight
    do if u and v are not both in the same tree
          add edge (u, v) into tree
          
return tree

代码实现

/**
 * @brief code for spanning tree
 * @author xiyan
 * @date 2014/07/01
 *
 */
#include <vector>
#include <iostream>
#include <algorithm>
namespace sptree{
using namespace std;
class Edge{
    public:
        int lft;
        int rht;
        int weight;
};
struct edgeComapre {
      bool operator() (Edge lft, Edge rht) { return (lft.weight < rht.weight);}
} edgeCompareObj;
class Kruskal{
    public:
        Kruskal(void):tot(0) { tmpClean(); }
        int init(const int &posSize);
        int insert(const class Edge nedge);
        int build(void);
        virtual ~Kruskal(void){ tmpClean();  }
    private:
        void tmpClean(void);
        int findSet(const int &pos);
        void makeUnion(const int &lft, const int &rht);
        vector<class Edge> edges;
        int *fatherA;
        int  posSize;
        int tot;
};

int Kruskal::insert(const class Edge nedge) {
        if(nedge.lft >= posSize || nedge.rht >= posSize)
            return - 1;
        edges.push_back(nedge);
        return 0;
}

void Kruskal::tmpClean(){
        edges.clear();
        if(fatherA){
            delete  fatherA;
            fatherA = NULL;
        }
}

int Kruskal::findSet(const int &pos){
    int rootPos;
    int currPos = pos;
    while(fatherA[currPos] != currPos){
        currPos = fatherA[currPos];
    }
    rootPos = currPos;
    currPos = pos;
    while(currPos != rootPos){
            int fatherPos = fatherA[currPos];
            fatherA[currPos] = rootPos;
            currPos = fatherPos;
    }
    return rootPos;
}

int Kruskal::init(const int &iposSize){
    if(iposSize <= 0)
            return -1;
    posSize = iposSize;
    if( NULL == ( fatherA = new int[posSize])){
            return -1;
    }

    for(int currPos = 0; currPos  < posSize; currPos++){
            fatherA[currPos] = currPos;
    }
    return 0;
}

int Kruskal::build(void){
    int ret = 0;
    sort(edges.begin(), edges.end(), edgeCompareObj);
    for(vector<class Edge>::iterator iter =  edges.begin(); iter != edges.end(); iter++){
            int lftRoot = findSet(iter->lft);
            int rhtRoot = findSet(iter->rht);
            if(lftRoot == rhtRoot){
                continue;
            }
            makeUnion(lftRoot, rhtRoot);
            ret += iter->weight;
    }
    tmpClean();
    return ret;
}

void Kruskal::makeUnion(const int &lft, const int &rht){
        fatherA[lft] = rht;
}
}

using namespace sptree;
int main(){
    class Kruskal *krup  = new class Kruskal;
    class Edge nedge;
    if(!krup){
            cout << "new kruskal fail" << endl;
            return -1;
    }
    if(krup->init(20) < 0){
        cout << "set max pos fail" << endl;
        return -1;
    }
    cout << "please input info for every edge" << endl;
    while(cin >> nedge.lft >> nedge.rht >> nedge.weight){
                    krup->insert(nedge);
                    cout << "please input info for every edge" << endl;
    }
    cout << krup->build() << endl;
    return 0;
}


普里姆(Prim算法)算法

概述

      图论中的一种算法,可在加权连通图里搜索最小生成树。意即由此算法搜索到的边子集所构成的树中,不但包括了连通图里的全部顶点(英语:Vertex (graph theory)),且其全部边的权值之和亦为最小。该算法于1930年由捷克数学家沃伊捷赫&middot;亚尔尼克(英语:Vojtěch Jarn&iacute;k)发现;并在1957年由美国计算机科学家罗伯特&middot;普里姆(英语:Robert C. Prim)独立发现;1959年,艾兹格&middot;迪科斯彻再次发现了该算法。所以,在某些场合,普里姆算法又被称为DJP算法、亚尔尼克算法或普里姆-亚尔尼克算法。安全

伪代码

/*该算法依赖于定理23.2*/
MST-PRIM(G,w, r)          /*初始化非起始节点*/
for each u 属于 V - {r}
         u.key = 无穷大
         u.father = NIL
         push u into Q 
         
r.key = 0;                  /*初始化起始节点*/         
r.father = r;
push u into Q 

while Q != 空
        u =  pop element from Q wich with  min key
        for each edge (u, v)
              if v not in Q  and w(u , v) < v.key
                    v.key = w(u , v)
                    v.father = u

Boruvka算法

参考文章

  1. <算法导论>ui

  2. http://baike.baidu.com/view/288214.htm?fr=aladdinspa

  3. http://www.cnblogs.com/biyeymyhjob/archive/2012/07/30/2615542.htmlcode

IN BUILDING