演算法筆記 並查集

2021-08-07 17:11:21 字數 3048 閱讀 4149

專題:並查集

一、引入

在一些有n個元素的集合應用問題中,我們通常是在開始時讓每個元素構成乙個單元素的集合,然後按一定順序將屬於同一組的元素所在的集合合併,其間要反覆查詢乙個元素在哪哪個集合中。

該問題看似並不複雜,但資料量極大,若用正常的資料結構來描述的話,往往超過了空間的限制,計算機無法承受;而且複雜度較高,實現過程較複雜。因此,只能採用一種特殊資料結構——並查集來描述。

二、定義

並查集是一種用於分離集合操作(管理元素分組情況)的抽象資料型別。它所處理的是「集合」之間的關係,即動態地維護和處理集合元素之間複雜的關係。

當給出兩個元素的乙個無序對(a,b)時,需要快速「合併」a和b分別所在的集合,這其間需要反覆「查詢」某元素所在的集合。「並」、「查」和「集」三字由此而來。在這種資料型別中,n個不同的元素被分為若干組。每組是乙個集合,這種集合叫做分離集合。

三、結構

使用樹形結構實現,不過不是二叉樹。

集合中的每個元素對應乙個節點,每個集合對應一棵樹。在並查集中,哪個節點是哪個節點的父親以及樹的形狀等資訊無需多加關注,整體組成乙個樹形結構才是重要的。

四、基本操作

並查集支援查詢乙個元素所屬的集合 以及 兩個元素各自所屬的集合的合併 兩種操作。

1.      初始化:

使用n個節點表示n個元素,最開始時沒有邊。

2.      合併:

相當於將兩個集合合併為乙個集合(即求並集的過程),假定在此操作前兩個集合是分離的。

3.      查詢:

查詢兩個節點是否屬於同一集合(即他們是否有相同的根節點),我們需要沿著樹向上走,來查詢包含這個元素的樹的根是誰。如果兩個節點走到了同乙個根,則可說明它們屬於同一集合。

舉例:

五、優化

1. 當樹形結構發生「退化」,即趨於線性結構時,對並查集的基本操作的複雜度就會非常高。因此,有必要優化儲存結構,想辦法避免「退化」的發生。

2. 考慮高度合併的方法:對每棵樹,記錄樹的高度rank;合併時,如果兩棵樹的rank不同,那麼從rank小的向rank大的連邊。

3. 並查集的路徑壓縮(乙個重要且典型的方法)

(1)路徑壓縮實際上是在找完根結點之後,在遞迴回來的時候順便把路徑上元素的父親指標都指向根結點。

(2)以上圖為例,我們在「合併5和3」的時候,不是簡單地將5的父親指向3,而是直接指向根節點1,如圖:

(3)在使用這種簡化的方法時,為簡便起見,即使樹的高度發生了變化,也不修改rank的值。

4. 複雜度分析:o(ɑ(n)),是乙個」均攤複雜度」,比o(log(n))還要快。

六、**實現(以陣列實現為例)

#include #define maxn 100005

int n,m,q; //建立含n個元素的集合(編號1~n),進行m次合併、q次查詢

int r1,r2,x,y; //將x和y節點所屬集合合併

int parent[maxn]; //parent[i]表示元素i的父親節點

int rank[maxn]; //樹的高度

void init(int n) //初始化並查集}/*

int find(int x) //查詢樹的根(非遞迴實現)

*/int find(int x) //查詢樹的根(遞迴實現)

void union(int x,int y) //合併節點x和y所屬的集合

p[maxn];

void init(int n)

}int find(int n)

void union(int x,int y)

stu[maxn];

void init(int n)

}int findroot(int n)

void union(int x,int y)

f[maxn][maxn],k1,k2;

void init(node f[maxn])//初始化點陣(並查集)

}}node findroot(node k) //找根節點

int main()

a[3*maxn];

void init(int n)

}int findpre(int n)

void union(int x,int y)

if(d==1) //"x和y屬於同一類"的資訊

}else //"x吃y"的資訊}}

printf("%d\n",ans);

return 0;

}

***********************************====拓展部分***********************************===

1. 求無向圖的連通分量

求無向圖連通分量是個非常常用的演算法。通過並查集可以使得空間上省去對邊的儲存,同時時間效率又是很高的。

需要特別指出的是,如果用鍊錶來實現的話,最後任何在同乙個集合(即連通塊)中的元素,其代表指標的值都是相等的。而採用有根樹來實現的話,演算法結束後,留下的依然是樹的關係,因此如果希望每個元素都指向它的根的話,還需要對每個節點進行一次find操作,這樣每個節點的父節點都是代表此集合的節點。在某些統計問題中,往往需要這樣做。

2. kruskal最小生成樹演算法

此經典演算法的思想是將樹上的邊按照邊權排序,然後從小到大分析每一條邊,如果選到一條邊e=(v1,v2),且v1和v2不在乙個連通塊中,就將e作為最小生成樹的一條邊,否則忽略e。這其中明顯就包含了並查集的演算法。kruskal演算法也只有在結合了並查集後才能說是個高效的演算法。

並查集演算法筆記

並查集是一種用來管理元素分組情況的資料結構,並查集可以高效的進行如下操作 使用樹狀結構來實現 在樹形資料結構中,如果發生了退化的情況,複雜度就會變得很高。在並查集中,可以按照如下方法避免退化 並查集複雜度平均下來每次查詢和合併的複雜度都是常數的 阿克曼函式 leetcode 547.friend c...

演算法筆記 並查集

先給出源 和輸入輸出測試結果 includeusing namespace std const int n 110 int father n 存放父親節點 bool isboot n 標記時否為根節點 int findfather int x return x void union int a,in...

《演算法筆記》9 6 並查集

一.定義 實現 int father n father i j 表示 j 的父親結點是 i father i i 表示元素 i 是該集合的根結點 二.基本操作 1.初始化 for int i 1 i n i father i i 2.查詢 非遞迴實現 int findfather int x 遞迴實...