1. 程式人生 > >算法實踐--最小生成樹(Kruskal算法)

算法實踐--最小生成樹(Kruskal算法)

res char inf ack 不同 代碼示例 pre 一起 oid

什麽是最小生成樹(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]; else return 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; }

算法實踐--最小生成樹(Kruskal算法)