最小生成樹之kruskal演算法(附程式碼)
阿新 • • 發佈:2018-12-24
prim演算法是通過找距離最近的節點來擴充最小生成樹的,稠密圖選擇prim演算法效率比較高,但是對於稀疏圖呢,prim演算法就顯的比較雞肋了。對於稀疏圖,有一個叫做kruskal的演算法。此演算法求稀疏圖的效率比較高,時間複雜度為O(ElogE)。
kruskal演算法主要通過找最小的邊來合併節點來一步步的生成樹的,他一開始將每個節點看成一棵樹,然後通過邊的關係來進行節點的合併,這裡需要對給出的邊進行升序排序,然後再檢查邊所聯絡的兩個節點是否在同一集合,如果不在同一集合則將兩個集合合併,直到整個森林變為一顆樹為止。
程式碼如下:
#include <cstdio> #include <cstring> #include <algorithm> #include <iostream> #include <vector> using namespace std; const int maxn=1005; int n,m; struct edge { int s,e; int len; }; edge e[maxn]; int a[maxn]; vector <edge> ve; //記錄納入的邊 //初始化 int init () { for (int i=1;i<=n;i++) a[i]=i; } //查詢父節點 int finds(int re) { if(re==a[re]) return re; return a[re]=finds(a[re]); } //將兩個集合聯合 bool unit (int x,int y) { int temp1=finds(x); int temp2=finds(y); if(temp1!=temp2) { a[temp1]=temp2; return true; } return false; } int compare (edge a,edge b) { return a.len<b.len; } //krusal演算法 int kruskal () { //先進行排序 sort(e,e+m,compare); int num=n; int sum=0; for (int i=0;i<m&&num>1;i++) { //判斷是否在同一集合中,在同一集合就不再計入 if(unit(e[i].s,e[i].e)) { ve.push_back(e[i]); sum+=e[i].len; num--; } } if(num==1) printf("最小生成樹的權值和為%d\n",sum); else printf("不存在最小生成樹\n"); } //遍歷納入的邊 void traverse () { printf("納入的邊為:\n"); for (int i=0;i<ve.size();i++) printf("%d<->%d 長度為%d\n",ve[i].s,ve[i].e,ve[i].len); } int main() { scanf("%d%d",&n,&m); init(); for (int i=0;i<m;i++) { scanf("%d%d%d",&e[i].s,&e[i].e,&e[i].len); } kruskal(); traverse (); return 0; } /*執行結果 6 15 1 2 5 1 3 3 1 4 7 1 5 4 1 6 2 2 3 4 2 4 6 2 5 2 2 6 6 3 4 6 3 5 1 3 6 1 4 5 10 4 6 8 5 6 3 最小生成樹的權值和為12 納入的邊為: 3<->5 長度為1 3<->6 長度為1 1<->6 長度為2 2<->5 長度為2 2<->4 長度為6*/