演算法導論——最小產生樹

來源:互聯網
上載者:User

標籤:去掉   順序   時間複雜度   span   alt   col   輕量級   切割   name   

  對於一個連通圖來說,我們可以去掉其中一些邊依然保持其連通的性質,在這些圖中存在一個或多個圖,他們的路徑總和是最小的,這樣的圖必然是樹。因為,如果說圖中存在環,則去掉環的一條邊依然可以保證連通性,這與總路徑和最小是矛盾的。這樣的圖被稱為最下產生樹。城市間鋪設電路就可以利用最小產生樹來進行規劃。

的黑色路徑構成了最小產生樹,去掉bc並加入ah也是一棵最小產生樹,可見一個圖的最小產生樹並不一定是唯一的。

  最小產生樹可以使用安全邊的策略進行產生:假設集合A是最小產生樹的子集,我們可以找到一條邊加入到A中,依然保持A是最小產生樹的子集,這樣的邊就被稱為安全邊。為了尋找安全邊,我們定義下方黑色部分ab與de邊構成了集合A,(A,V-A)稱為G的一個切割。如果一條邊(v,u)一個端點屬於A而另一個端點屬於V-A,則稱它為橫跨切割(A,V-A)。在橫跨切割的邊中,可以找到一條或多條權重最小的邊,該邊稱為輕量級邊,輕量級邊即是安全邊。因為A一定要與V-A部分產生串連,這就必須要通過橫跨切割的邊,而輕量級邊是其中最短的,所以必定屬於最小產生樹。所示ah bj bc cd df ef為橫跨切割的邊,其中cd為輕量級邊。為了尋找輕量級邊,有兩種基於貪心策略的演算法。

Kruskal演算法

  Kruskal演算法的思想是,尋找串連森林中兩棵不同樹的邊裡面的最短邊作為安全邊加入集合A。可以使用不相交集合來維護這樣的結構,對每個結點建立一棵樹。通過FIND-SET來返回結點屬於哪棵樹,有邊加入集合A時合并u和v所在的樹。時間複雜度可表示為O(ElgV)。

圖中所示為各邊按照長度順序不斷遍曆檢查是否要加入到集合A中,直到遍曆完所有的邊結束。

#include<stdio.h>#include<vector>#include <algorithm>using namespace std;#define SIZE 10class Node{public:    int rank;    Node *p;};class Road{public:    int u;    int v;    int weight;};int G[SIZE][SIZE];//鄰接矩陣,參數初始化略Node nodes[SIZE];void makeSet(Node x){    x.p = &x;    x.rank = 0;}void Union(Node x,Node y){    if(x.rank > y.rank)        y.p = &x;    else{        x.p = &y;        if(x.rank == y.rank)            y.rank++;    }}Node* findSet(Node x){    if(x.p != &x)        x.p = findSet(*x.p);    return x.p;}bool com (Road a,Road b) {    return (a.weight<b.weight); //升序排列}vector<Road> MSTKruskal(){    vector<Road> A;    int i,j;    vector<Road> roads;    for(i = 0; i < SIZE; i++){        nodes[i] = *new Node();        makeSet(nodes[i]);//每棵樹包含一個結點    }    for(i = 0; i < SIZE; i++){        for(j = i + 1; j < SIZE; j++){            if(G[i][j] != 0){                Road road = *new Road();                road.u = i;                road.v = j;                road.weight = G[i][j];                roads.push_back(road);            }                        }    }    sort(roads.begin(),roads.end(),com);//對所有路徑進行降序排列    for(i = 0; i < roads.size(); i++){        Road road = roads[i];        if(findSet(nodes[road.u]) != findSet(nodes[road.v])){            A.push_back(road);            Union(nodes[road.u],nodes[road.v]);        }    }    return A;}
Prim演算法

  Prim演算法的思路是從根結點開始加入集合A,不斷尋找A與V-A相連邊中最短的,即橫跨(A,V-A)的輕量級邊。可以將V-A到A距離的最小值儲存到優先隊列Q中來減少每次遍曆尋找最短邊的時間。優先隊列可以通過最小二元堆積或者斐波那契堆來實現,前者的漸進時間為O(ElgV)後者改進為O(E+VlgV)

#include<stdio.h>#include<vector>using namespace std;#define SIZE 10#define INFI 10000class Road{public:    int u;    int v;    int weight;};int G[SIZE][SIZE];//鄰接矩陣,參數初始化略vector<Road> MSTPrim(int root){    vector<Road> A;    int i;    Road roads[SIZE];//記錄到達A的最短路徑    for(i = 0; i < SIZE; i++){        roads[i] = *new Road();        if(G[root][i] != 0){            roads[i].u = root;            roads[i].v = i;            roads[i].weight = G[root][i];        }        else            roads[i].weight = INFI;    }    while(A.size() != SIZE - 1){        int min = 0;        for(i = 1; i < SIZE; i++){            if(roads[i].weight < roads[min].weight && roads[i].weight > 0)                min = i;//尋找最短的路徑        }        A.push_back(roads[min]);        roads[min].weight = -1;//表示該點已在A中        for(i = 0; i < SIZE; i++){            if(G[min][i] < roads[i].weight)                roads[i].weight = G[min][i];//更新到達A的最短長度        }    }    return A;}

 

演算法導論——最小產生樹

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.