最小生成樹

簡介

        在一給定的無向圖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

相關文章
相關標籤/搜索
本站公眾號
   歡迎關注本站公眾號,獲取更多信息