昨天看了幾篇有關並查集的**,對理解並查集很有幫助,在這裡寫篇部落格來記錄下自己對並查集的簡單理解,以及並查集的簡單運用。
並查集的分析
並查集的概念主要是處理集合問題(或可抽象成集合概念的問題),首先數學上的集合概念做個初步的分析
首先,我們從數學的角度給出等價關係和等價類的定義:
定義1:如果集合s中的關係r是自反的,對稱的,傳遞的,則稱他為乙個等價關係。
——自反:x=x;
——對稱:若x=y,則y=x;
——傳遞:若x=y、y=z,則x=z。?要求:x、y、z必須要同乙個子集中。
定義2:如果r是集合s的等價關係。對於任何x∈s,由[x]r=給出的集合[x]rs
稱為由x∈s生成的乙個r的等價類。
定義3:若r是集合s上的乙個等價關係,則由這個等價關係可產生這個集合的唯一劃分。
即可以按r將s劃分為若干不相交的子集s1,s2,s3,s4,…,他們的並即為s,則這
些子集si變稱為s的r等價類。
劃分等價類的問題的提法是:要求對s作出符合某些等價性條件的等價類的劃分,已知
集合s及一系列的形如「x等價於y」的具體條件,要求給出s的等價類的劃分,符合所
列等價性的條件。(我們上面提到的聯絡,即可認為是乙個等價關係,我們就是要將
集合s劃分成n個聯絡的子集,然後再判斷x,y是否在乙個聯絡子集中。)
這三個基本定義對理解並查集的概念及相應的操作非常有幫助。
並查集的操作主要有三個:
1.劃分等價類,這個在預處理的時候進行,很簡單,就是有多少元素就劃分多少等價類即pre[i]=i,很好理解,為下面的合併做好準備。
2.找根節點(溯源) 我通常用函式root(int x)來實現找x的最終祖先。
3.合併,就是將兩個集合合為乙個集合。
用演算法實現三個方法:
1.預處理
void init(int n)
}
2.溯源
int root(int x)
return pre[x];
}
溯源這裡是有優化的,判斷兩個元素是否屬於同一集合需要o(n)的時間,但這裡可以使用路徑壓縮來進行優化, 路徑壓縮實際上是在找完根結點之後,在遞迴回來的時候順便把路徑上元素的父親指標都指向根結點
3.合併
void merge(int a,int b)
}
合併就是先找到a和b各自的源,然後把源連起來則兩個集合就實現了合併。
一上這三個函式就是並查集的核心思想和方法,以後的解題關鍵就是能將問題抽象成集合並應用並查集來解決了。
並查集的應用
下面我們就以以上並查集的思想解決乙個實際問題來簡單運用一下。
以解決 hdu 1232為例。
題目在這裡就不再贅述了,經過上面的分析,此題就變的很簡單了。
n個頂點連通至少需要n-1條邊,這個是學計算機的都曉得的基礎知識(樹)。此問題總需要的最少邊為total = n-1,每連線兩個不在同一集合中的頂點時,所需要的邊數減一(--total),ok了,最後連完之後的total就是還需要的邊數(也是現在的等價類數目),這個應該也很好理解。
程式實現:
#include int total;
int pre[1001];
void init(int n)
}int root(int x)
return pre[x];
}void merge(int a,int b)
}int main(){
int n,m,i,st,end;
while(scanf("%d",&n) && n){
scanf("%d",&m);
init(n);
total = n-1;
for(i=0;i
並查集的優化及應用
2018 05 01 15 13 08 並查集是乙個時空複雜度非常優越的資料結構,並且通過優化後其複雜度為。並查集的優化主要有兩個方面 路徑壓縮 按rank合併 public class unionfindset public int find int i public boolean union ...
並查集及種類並查集
b站 並查集int find root int x return x int hebing int x,int y return0 檢驗 include using namespace std const int n 100 const int m 200 int parent n deep n i...
並查集的應用
特點 1,都有乙個陣列儲存它的根節點 2,用vis陣列儲存其是否訪問過 3,如果cnt 1 說明該圖是連通的。連通圖中只有根節點的父節點是自身,cnt應該是1 解決的題目 1,題目已經給了圖上頂點之間的關係,通過頂點之間的關係,求連通分量的個數 include stdio.h int bin 100...