最小生成樹演算法Kruskal
阿新 • • 發佈:2021-07-06
目錄
最小生成樹演算法
最小樹定義:
- 給定網路\(G=(N,E,W)\),設\(T=(N,E')\)為\(G\)的一個支撐樹,令\(W(T)=\sum_{e\in E'}W(e)\)為\(T\)的權(或長)。\(G\)中權最小的支撐樹稱為\(G\)的最小樹。
1、Kruskal
- 並查集:用一個元素代表一組元素,用於查詢不同元素是否屬於同一組,以及合併組
int par[MAXN];//每個元素所在集合代表元素的編號 int rnk[MAXN];//所在集合的大小; //初始化 for(int i=0; i<n; i++){ par[i]=i; rnk[i]=1; } int find(int x){ //查詢x所在集合的代表元 if(par[x]==x)//到達根 return x; //直接路徑壓縮,忽略節點之間關係,把節點掛到根上 else par[x] = find(par[x]); } bool unite(int x,int y){ //把一個分組掛到另一個分組,返回合併是否成功 x=find(x); y=find(y); if(x == y) return false; if(rnk[x]>rnk[y]){//把小樹掛在大樹上,防止出現高樹 par[y]=x; rnk[x] = (rnk[y] += rnk[x]); //可以直接rnk[x]+=rnk[y] } else{ par[x]=y; rnk[y] = (rnk[x] += rnk[y]); //可以直接rnk[y]+=rnk[x] } return true; }
1.1 演算法簡介
Kruskal是1956年首次提出的求最小生成樹的演算法,後來Edmonds把該演算法稱為貪心演算法,其基本思路就是從G中的m條邊中選取n-1條權儘可能小的邊,使其不構成迴路,從而構成一個最小樹。
- 第一步:把圖按照邊權的大小從小到大排列起來,初始化一個已選中的邊集,或計數器
- 第二步:不斷從加入之後不構成環的邊中選擇權最小的邊加入邊集
- 第三步:如果邊集中邊的數量達到n-1,則停止,否則繼續選邊加邊
1.2 C++實現
祝看到這裡的你生活愉快,謝謝#include<bits/stdc++.h> using namespace std; const int MAXN = 1e6; struct edge{ int u,v,w; //過載比較符,為了按照邊權排序 bool operator < (const edge& t){ return w < t.w; } }e[MAXN]; int m,n; int par[MAXN];//每個元素所在集合代表元素的編號 int rnk[MAXN];//所在集合的大小; void init(int n){ for(int i=0; i<n; i++) par[i]=i; } int find(int x){ if(par[x]==x) return x; else par[x] = find(par[x]); } bool unite(int x,int y){ x=find(x);y=find(y); if(x == y) return false; par[x]=y; return true; } int kruskal(){ std::sort(e+1,e+1+m); int cnt=0,minum_tree_size=0; for(int i=1; i<=m; i++){ if(unite(e[i].u,e[i].v)){ minum_tree_size += e[i].w; if(++cnt == n-1)break; } } return cnt==n-1? ans : -1; } int main(){ scanf("%d%d", &m,&n); init(n); for(int i=1;i<=m;i++)scanf("%d%d%d",&e[i].u,&e[i].v,&e[i].w); printf("%d\n",kruskal()) return 0; }