Spanning Tree
来源:互联网 发布:淘宝联盟如何领优惠券 编辑:程序博客网 时间:2024/05/17 04:16
转自: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) 。
- const int V = 100, E = 1000;
-
- struct Edge {int a, b, c;} e[E];
- bool operator<(const Edge& e1, const Edge& e2) {return e1.c < e2.c;}
-
- int p[V];
- int init() {for (int i=0; i<V; ++i) p[i] = i;}
- int find(int x) {return x == p[x] ? x : (p[x] = find(p[x]));}
- void union(int x, int y) {p[find(x)] = find(y);}
-
- void Kruskal()
- {
- init();
-
-
- sort(edge, edge+E);
-
-
- int i, j;
- for (i = 0, j = 0; i < V-1 && j < E; ++i)
- {
-
- while (find(e[j].a) == find(e[j].b)) j++;
-
-
- union(e[j].a, e[j].b);
-
-
- cout << "起點:" << e[j].a
- << "終點:" << e[j].b
- << "權重:" << e[j].c;
-
- j++;
- }
-
- if (i == V) cout << "得到最小生成樹";
- else cout << "得到最小生成森林";
- }
迴圈的部份也可以寫成這樣。
-
- for (i = 0, j = 0; i < V-1 && j < E; ++j)
- {
-
- if (find(e[j].a) == find(e[j].b)) continue;
-
-
- union(e[j].a, e[j].b);
-
-
- cout << "起點:" << e[j].a
- << "終點:" << e[j].b
- << "權重:" << e[j].c;
-
- i++;
- }
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 ,得到更低的時間複雜度。
- int w[9][9];
- int d[9];
- int parent[9];
- bool visit[9];
-
- void prim()
- {
- for (int i=0; i<9; i++) visit[i] = false;
- for (int i=0; i<9; i++) d[i] = 1e9;
-
- d[0] = 0;
- parent[0] = 0;
-
- for (int i=0; i<9; i++)
- {
- int a = -1, b = -1, min = 1e9;
- for (int j=0; j<9; j++)
- if (!visit[j] && d[j] < min)
- {
- a = j;
- min = d[j];
- }
-
- if (a == -1) break;
- visit[a] = true;
-
- for (b=0; b<9; b++)
-
- if (!visit[b] && w[a][b] < d[b])
- {
- d[b] = w[a][b];
- parent[b] = a;
- }
- }
- }
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
節點編號0123456789距離樹的距離0∞∞∞∞∞∞∞∞∞ 0 0