Spanning Tree

转自:http://www.csie.ntnu.edu.tw/~u91029/SpanningTree.html

先收藏了,以后好好看一下。

Spanning Tree / Spanning Forest

中譯「生成樹」,從一張圖上分離出一棵包含圖上所有點的樹,便是這張圖的生成樹。一張圖的生成樹可能會有很多種。

當一張圖完全連通,必然有生成樹。當一張圖有部份不連通,則沒有生成樹,而是許多棵「生成子樹」所構成的「生成森林」。宛如 DFS tree 與 DFS forest 的關係。

生成樹也可以有權重。當圖上每條邊都有權重時,生成樹的權重為樹上每條邊的權重總和。

Minimum Spanning Tree

中譯「最小生成樹」。權重最小的生成樹就是最小生成樹。一張圖的最小生成樹可能會有很多種。

Minimum Spanning Tree: 
Kruskal's Algorithm

程度★ 難度★★

用途

求出無向圖的其中一棵最小(大)生成樹。若是圖不連通,則是求出其中一叢最小(大)生成森林。

演算法

一、兩棵 MST ,要合併成一棵 MST 時,以兩棵 MST 之間權重最小的邊進行連結,當然會是最好的。

二、三棵 MST ,要合併成一棵 MST 時,先連結其中兩棵連結權重最小的 MST ,然後才連結第三棵,總是比較好。

三、一個單獨的點,可以視作一棵 MST 。

由以上三點,可以歸納出一個 greedy 演算法:以權重最小的邊連結各棵 MST ,一定比較好。

一、一開始圖上每一個點,各自是一棵最小生成子樹MSS。
二、圖上所有邊,依照權重大小,由小到大排序。
三、依序嘗試圖上所有邊,作為最小生成樹(森林)上的邊:
 甲、兩端點分別位於兩棵MSS,也就是產生了橋:
   用這條邊連接兩棵MSS,合併成一棵MSS。
   這條邊會是最小生成樹(森林)上的邊。
 乙、兩端點皆位於同一棵MSS,也就是產生了環:
   捨棄這條邊。

每次選中的邊,都是 MST 上的邊。沒有選中的邊,不論這張圖以後又增加了多少邊,絕不會成為 MST 上的邊。

時間複雜度

一、排序圖上所有邊,需時 O(ElogE) 。

二、連接 MSS ,一般是運用「 Disjoint-set Forest 」,需時 O(E*α(E,V)) 。

故時間複雜度為 O(ElogE) 。

 
  1. const int V = 100E = 1000;
  2.  
  3. struct Edge {int abc;} e[E];    // edge list
  4. bool operator<(const Edgee1const Edgee2) {return e1.c < e2.c;}
  5.  
  6. // disjoint-sets forest
  7. int p[V];
  8. int init() {for (int i=0i<V; ++ip[i] = i;}
  9. int find(int x) {return x == p[x] ? x : (p[x] = find(p[x]));}
  10. void union(int xint y) {p[find(x)] = find(y);}
  11.  
  12. void Kruskal()
  13. {
  14.     init();
  15.  
  16.     // 圖上所有邊,依照權重大小,由小到大排序。
  17.     sort(edgeedge+E); // O(NlogN)
  18.  
  19.     // 依序找出最小生成樹上的V-1條邊。
  20.     int ij;
  21.     for (i = 0j = 0i < V-1 && j < E; ++i)
  22.     {
  23.         // 產生環,則捨棄。直到產生橋。
  24.         while (find(e[j].a) == find(e[j].b)) j++;
  25.  
  26.         // 產生橋,則以此邊連接兩棵MSS。
  27.         union(e[j].ae[j].b);
  28.  
  29.         // 印出最小生成樹(森林)上的邊。
  30.         cout << "起點:" << e[j].a
  31.             << "終點:" << e[j].b
  32.             << "權重:" << e[j].c;
  33.  
  34.         j++;    // 別忘記累計索引值。也可以寫入迴圈。
  35.     }
  36.  
  37.     if (i == Vcout << "得到最小生成樹";
  38.     else        cout << "得到最小生成森林";
  39. }

迴圈的部份也可以寫成這樣。

 
  1.     // 窮舉圖上所有邊,嘗試作為最小生成樹(森林)。
  2.     for (i = 0j = 0i < V-1 && j < E; ++j)
  3.     {
  4.         // 產生環,則捨棄。
  5.         if (find(e[j].a) == find(e[j].b)) continue;
  6.  
  7.         // 產生橋,則以此邊連接兩棵MSS。
  8.         union(e[j].ae[j].b);
  9.  
  10.         // 印出最小生成樹(森林)上的邊。
  11.         cout << "起點:" << e[j].a
  12.             << "終點:" << e[j].b
  13.             << "權重:" << e[j].c;
  14.  
  15.         i++;    // 別忘記累計索引值。不可以寫入迴圈。
  16.     }

UVa 908 10369

Minimum Spanning Tree: 
Prim's Algorithm

程度★ 難度★★

用途

求出無向圖的其中一棵最小(大)生成樹。

演算法

與「 Shortest Path: Dijkstra's Algorithm 」的概念大致相同。

主要的差異是: Dijkstra's Algorithm 屢次找不在樹上、離根最近的點, Prim's Algorithm 屢次找不在樹上、離樹最近的點。

另外一個差異是:最短路徑樹擁有特定起點,而最小生成樹可以選定任何一點作為樹根。

時間複雜度

圖的資料結構為 adjacency matrix 的話,便是 O(V^2) ;圖的資料結構為 adjacency lists 的話,還是 O(V^2) 。

就和 Dijkstra's Algorithm 一樣, Prim's Algorithm 也可以使用 Fibonacci Heap 、 Priority Queue ,得到更低的時間複雜度。

 
  1. int w[9][9];    // adjacency matrix
  2. int d[9];       // 紀錄目前的MST到圖上各點的距離
  3. int parent[9];  // 紀錄各個點在MST上的父親是誰
  4. bool visit[9];  // 紀錄各個點是不是已在MST之中
  5.  
  6. void prim()
  7. {
  8.     for (int i=0i<9i++) visit[i] = false;
  9.     for (int i=0i<9i++) d[i] = 1e9;
  10.  
  11.     d[0] = 0;   // 可以選定任何點作為樹根,這裡以第零點作為樹根。
  12.     parent[0] = 0;
  13.  
  14.     for (int i=0i<9i++)
  15.     {
  16.         int a = -1b = -1min = 1e9;
  17.         for (int j=0j<9j++)
  18.             if (!visit[j] && d[j] < min)
  19.             {
  20.                 a = j;  // 記錄這一條邊
  21.                 min = d[j];
  22.             }
  23.  
  24.         if (a == -1break// 與起點相連通的MST都已找完
  25.         visit[a] = true;
  26. //      d[a] = 0;           // 註解後,得到MST每條邊權重。
  27.  
  28.         for (b=0b<9b++)
  29.             // 以下與Dijkstra's Algorithm略有不同
  30.             if  (!visit[b] && w[a][b] < d[b])
  31.             {
  32.                 d[b] = w[a][b]; // 離樹最近,不是離根最近。
  33.                 parent[b] = a;
  34.             }
  35.     }
  36. }

UVa 10034 10147 10307 10397 10600 10842

© 2010 tkcn . All rights reserved.

1
10
16
16
16
2
11
13
11
2
13
0
1
2
3
4
5
6
7
8
9
節點編號 0 1 2 3 4 5 6 7 8 9
距離樹的距離 0


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值