什么是最小生成樹(Minimum Spanning Tree)
每兩個端點之間的邊都有一個權重值,最小生成樹是這些邊的一個子集。這些邊可以將所有端點連到一起,且總的權重最小
下圖所示的例子,最小生成樹是{cf, fa, ab} 3條邊
?
Kruskal算法
用到上一篇中介紹的不相交集合(并查集)
首先,定義V是端點的集合,E是邊的集合,A為要求的最小生成樹集合
- 初始A為空集合,每個端點都作為單獨的不相交集合
- 將所有邊根據其權重進行排序
- 對每條邊(v1, v2),如果其兩個端點數據不同的不相交集,則將該邊加到集合A中,同時將v1和v2合并
- 最終得到的A即為最小生成樹
?
生成過程的示例圖
?
C++代碼示例
?
struct Edge {char vertex1;char vertex2;int weight;Edge(char v1, char v2, int w):vertex1(v1), vertex2(v2), weight(w) {} };struct Graph {vector<char> vertice;vector<Edge> edges; };unordered_map<char, char> PARENT; unordered_map<char, int> RANK;char find(char vertex) {if (PARENT[vertex] == vertex) return PARENT[vertex];elsereturn find(PARENT[vertex]); }void MST(Graph& g) {vector<Edge> res;for (auto c : g.vertice) {PARENT[c] = c;RANK[c] = 0;}sort(g.edges.begin(), g.edges.end(), [](Edge x, Edge y) {return x.weight < y.weight;}); // O(E*log(E))for (Edge e : g.edges) { // O(E)char root1 = find(e.vertex1); // 最差O(E),因為有記錄深度,Find可以認為很快char root2 = find(e.vertex2);if (root1 != root2) {res.push_back(e);if (RANK[root1] > RANK[root2]) {PARENT[root2] = root1;RANK[root1]++;} else {PARENT[root1] = root2;RANK[root2]++;}}}for (Edge e : res) {cout << e.vertex1 << " -- " << e.vertex2 << " " << e.weight << endl;} }void Union( char vertex_1, char vertex_2 ) { }int main() {char t[] = {'a', 'b', 'c', 'd', 'e', 'f'};Graph g;g.vertice = vector<char>(t, t + sizeof(t)/sizeof(t[0]));g.edges.push_back(Edge('a', 'b', 4)); // 稀疏圖用鏈來表示(E = O(V)) g.edges.push_back(Edge('a', 'f', 2)); // 如果是密集圖(E = O(V*V)), 用矩陣來表示g.edges.push_back(Edge('f', 'b', 5)); // 大部分感興趣的圖是稀疏的g.edges.push_back(Edge('c', 'b', 6));g.edges.push_back(Edge('c', 'f', 1));g.edges.push_back(Edge('f', 'e', 4));g.edges.push_back(Edge('d', 'e', 2));g.edges.push_back(Edge('d', 'c', 3));MST(g);return 0; }
?