在計算機科學中,並查集是一種樹型的數據結構,用於處理一些不交集的合併及查詢問題。有一個聯合-查找算法(union-find algorithm)定義了兩個用於次數據結構的操做:node
- Find:肯定元素屬於哪個子集。它能夠被用來肯定兩個元素是否屬於同一子集。
- Union:將兩個子集合併成一個集合。
for(int i=1;i<=n;i++) f[i]=i;
int find(int x) { while(f[x]!=x) x=f[x]; return x; }
void Union(int x1,int x2) { int t1=find(x1); int t2=find(x2); if(t1!=t2) f[t2]=t1; }
上面的代碼看似簡潔,可是每一次find操做的時間複雜度爲O(H),H爲樹的高度,因爲咱們沒有對樹作特殊處理,因此樹的不斷合併可能會使樹嚴重不平衡,最壞狀況每一個節點都只有一個子節點。c++
因此在find函數裏採用路徑壓縮。算法
int find(int x) //查找x元素所在的集合,回溯時壓縮路徑 { if (x != f[x]) { f[x] = find(f[x]); //從x結點搜索到祖先結點所通過的結點都指向該祖先結點 } return f[x]; }
洛谷P3367【模板】並查集數據結構
#include<bits/stdc++.h> using namespace std; int n,m; int f[10002]; int find(int x) { if(x!=f[x]) f[x]=find(f[x]); return f[x]; } void Union(int x1,int x2) { int t1=find(x1); int t2=find(x2); if(t1!=t2) //祖先不同 f[t2]=t1; //把t2的祖先變爲x1的祖先t1 } int main() { cin>>n>>m; for(int i=1;i<=n;i++) f[i]=i; for(int i=0;i<m;i++) { int z,x,y; cin>>z>>x>>y; if(z==1) Union(x,y); else { if(find(x)!=find(y))cout<<"N"<<endl; else cout<<"Y"<<endl; } } return 0; }
一個有n個結點的連通圖的生成樹是原圖的極小聯通子圖,期包含原圖的全部n個結點,且有保持圖連通的最少邊。函數
最小生成樹其實就是最小權重生成樹的簡稱。優化
Kruskal算法spa
例題:繁忙的都市code
#include<bits/stdc++.h> using namespace std; int n,m; int s,maxm; int p[100002]; struct node{ int u; int v; int c; }info[100002]; bool cmp(node x1,node x2) { if(x1.c!=x2.c)return x1.c<x2.c; else if(x1.u!=x2.u) return x1.u<x2.u; else return x1.v<x2.v; } int find(int x) //查找x元素所在的集合,回溯時壓縮路徑 { if (x!=p[x]) { p[x]=find(p[x]); } return p[x]; } void bcj(int x1,int x2)//把x2併入x1的集合 { int t1,t2;//存儲祖先節點 t1=find(x1); t2=find(x2); if(t1!=t2)p[t2]=t1; } int main() { cin>>n>>m;//n就是頂點數,m是邊數 for(int i=1;i<=n;i++) { p[i]=i; } for(int i=0;i<m;i++) { cin>>info[i].u>>info[i].v>>info[i].c; } sort(info,info+m,cmp); for(int i=0;i<m;i++)//遍歷全部的邊 { if(find(info[i].u)!=find(info[i].v)) { bcj(info[i].u,info[i].v);//把v併入u的集合 maxm=max(maxm,info[i].c); } } cout<<n-1<<" "<<maxm; return 0; }