《啊哈!演算法》 第八章 更多精彩的演算法

來源:互聯網
上載者:User

標籤:style   class   blog   code   http   tar   

 第一節  鏢局運鏢-圖的最小產生樹

所謂最小產生樹,就是在一個具有N個頂點的帶權連通圖G中,如果存在某個子圖G‘,其包含了圖G中的所有頂點和一部分邊,且不形成迴路,並且子圖G‘的各邊權值之和最小,則稱G‘為圖G的最小產生樹。

最小產生樹的三個性質

  • 最小產生樹不能有迴路
  • 最小產生樹可能是一個,也可能有多個
  • 最小生產樹的邊的個數等於頂點的個數減一

Kruskal 演算法(選邊法)

其核心思想是:首先按照邊的權值進行從小到大排序,每次從剩餘的邊中選擇權值較小且邊的兩個頂點不在同一個集合的邊(就是不會產生迴路的邊),加入到生產樹中,直到加入了n-1條邊為止。其實現是 貪心+並查集

#include <iostream>#include <vector>#include <algorithm>using namespace std;struct edge{    int u;    int v;    int w;    edge(int uu =0, int vv= 0, int ww = 0):u(uu),v(vv),w(ww){}    bool operator<(const edge& a) const{        return w < a.w;    }};vector<int> f;//建立並查集void make_set(int n){    for(int i = 0 ; i <= n; ++ i) f.push_back(i);}//查詢並查集int find_set(int x){    if(f[x] == x) return x;    else{        f[x] = find_set(f[x]);        return f[x];    }}//合并子集bool union_set(int x, int y){    int t1 = find_set(x), t2 = find_set(y);    if(t1!=t2){        f[t2] = t1;        return true;    }    return false;}int main(){    int n,m;    cin >> n >> m;    vector<edge> e(m+1);    for(int i = 1 ; i <= m; ++ i){        cin >> e[i].u >> e[i].v >> e[i].w;    }    sort(e.begin(),e.end());    int sum = 0, cnt = 0;    make_set(n);    for(int i = 1; i <= m ; ++ i){        if(union_set(e[i].u,e[i].v)){            cnt++;            sum+=e[i].w;        }        if(cnt == n-1) break;    }    cout<<sum<<endl;}
Kruskal演算法

 n代表頂點數,m代表邊數,對邊的快速排序時間複雜度是O(MlogM),在m條邊中找出n-1條邊是O(MlogN),所以Krusal演算法時間複雜度為O(MlogM+MlogN),通常M大於N,因此最終時間複雜度為O(MlogM)。

第二節 再談最小產生樹

Prim演算法(選點法)

其核心思想是:首先選擇任意一個頂點加入產生樹中,接下來找出一條邊添加到產生樹中,這需要枚舉每一樹頂點(已被選入生產樹的頂點)到每一個非樹頂點所有的邊,然後找出最短的邊加入到產生樹中。

#include <iostream>#include <vector>#include <algorithm>#define INF 100000using namespace std;int main(){    int n,m;    cin >> n >> m;    vector<vector<int> > e(n,vector<int>(n,INF));    for(int i = 0 ; i <  n; ++ i) e[i][i] = 0;    for(int i = 0 ; i < m; ++ i){        int u,v,w;        cin>> u >> v >> w;        --u;--v;        e[u][v] = w; e[v][u] = w;    }    vector<int> dist(e[0].begin(),e[0].end());    vector<bool> visit(n,false);    visit[0 ] = true;    int cnt = 1,sum = 0;    while(cnt < n){        int minw = INF,index = 0;        for(int i = 0; i  < n ; ++ i){            if(!visit[i] && dist[i] <  minw){                minw = dist[i];                index = i;            }        }        visit[index] = true;        cnt++;        sum +=minw;        for(int k = 0; k < n; ++ k){            if(!visit[k] && dist[k]> e[index][k]){                dist[k] = e[index][k];            }        }    }    cout<<sum<<endl;}
Prim演算法

上述Prim演算法如果使用鄰接矩陣來儲存圖的話,時間複雜度是O(N^2),觀察代碼很容易發現,時間主要浪費在每次都要遍曆所有點找一個最小距離的頂點,對於這個操作,我們很容易想到用堆來最佳化,使得每次可以在log層級的時間找到距離最小的點,然後使用鄰接表格儲存體圖,整個演算法的時間複雜度會降到O(mlogn)

 

Kruskal演算法更適用於稀疏圖,沒有使用堆最佳化的Prim演算法適用於稠密圖,使用了堆最佳化的Prim演算法更適用於稀疏圖

 

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在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.