1. 程式人生 > >Agri-Net的Kruskal演算法+並查集實現(按大小合併+路徑壓縮)

Agri-Net的Kruskal演算法+並查集實現(按大小合併+路徑壓縮)

Agri-Net的Kruskal演算法+並查集實現

演算法複雜度分析

    對所有的邊進行排序,排序複雜度為O(mlogm),隨後對邊進行合併,合併使用並查集,並查集使用link by size的方式實現,同時在find函式實現了路徑壓縮。每個元素第一次被執行find操作需要的複雜度為O(logm),總共m個元素,可以在迴圈中設定,如果已經有n-1條邊,那麼可以停止迴圈,時間複雜度為O(nlogm),前後兩個步驟的時間複雜度為O(mlogm+nlogm) ,而存在最小生成樹中的情況下,圖至少有n-1條邊,即m>=n-1,於是整體複雜度為O(mlogn)。即使對並查集做了路徑壓縮的優化,但是前面的排序過程仍然是演算法的瓶頸,因此演算法複雜度仍然是O(mlogn)。

程式碼實現

#include<cstdio>
#include<vector>
#include<queue>
#include<algorithm>
using namespace std;
// 邊
struct Edge {
	int from, to;
	int weight;
	Edge(int f, int t, int w) :from(f), to(t), weight(w) {}
	bool operator <(const Edge& e)const { return this->weight < e.weight;
} }; // 並查集 struct QuickUnion { vector<int> sz; // 表示集合大小的陣列 vector<int> parent; // 表示一個頂點的所在集合的根節點 QuickUnion(const int n) { sz.assign(n,0); for (int i = 0; i < n; ++i) parent.push_back(i); } // 尋找一個節點的根節點 int find(const int x) { if (x != parent[x]) parent[x] = find(parent[
x]);// 路徑壓縮 return parent[x]; } // 合併兩個節點所在的集合 bool unionNode(const int x, const int y) { int p1 = find(x); int p2 = find(y); if (p1 == p2) return false; if (sz[p1] >= sz[p2]){ sz[p1] += sz[p2]; parent[p2] = p1; } else{ sz[p2] += sz[p1]; parent[p1] = p2; } return true; } }; int Kruskal(vector<Edge> graph, int nodeNum) { QuickUnion qu(nodeNum); sort(graph.begin(),graph.end()); int MSTWeight = 0; int edgeCount = 0; for (auto&e : graph){ if (qu.unionNode(e.from, e.to)){ MSTWeight += e.weight; ++edgeCount; if (edgeCount == nodeNum - 1) break; } } return MSTWeight; } int main() { int edgeLen; while (scanf("%d",&edgeLen)!=EOF){ vector<Edge > graph; int curRow = 0, curCol = 0; while (curRow < edgeLen){ curCol = 0; while (curCol < edgeLen){ int tmp; scanf("%d", &tmp); if (curRow < curCol) graph.emplace_back(curRow, curCol, tmp); curCol++; } ++curRow; } printf("%d\n", Kruskal(graph,edgeLen)); } return 0; }