Disjoint
Set
Disjoint Set
並查集
Disjoint Set
- 給你\(n\)個集合,每個集合中分別有一個元素
編號為\(1\sim n\) - 你有兩種操作可以做:
-
Same(x,y):
問你編號x的元素和編號y的元素是否在同一個集合中 - Union(x, y):
將編號x的元素和編號y的元素,他們所在的集合進行合併
-
Same(x,y):
老大是誰
- 眾所周知,在某些道上有很鮮明的階級制度
- 當兩個互不認識的小弟遇到了
通常不會問對方是哪個組織
而是問「你老大是誰」
你老大是誰
X!羅大哥我老大
最高領導
- 報上名的老大通常是組織的最高領導者
- 大家就會知道你是哪個組織的
老大
黑吃黑
- 有的時候會發生黑吃黑
- 基本上就是組織A把另一個組織B給併吞了
A老大
B老大
A老大
(原)B老大
Disjoint Set
模擬道上系統
3
1
2
4
1 | 2 | 3 | 4 |
---|---|---|---|
3 | 1 | 3 | 3 |
parent array
誰是老大
- 一開始所有人都是自己的老大
- 透過find去問自己上司的老大是誰,一直問到某個人他的上司是自己,那他就是老大
int parent[1000005];
void init(int n){
for(int i = 1; i <= n ++i)
parent[i] = i;
}
int Find(int x){
if(parent[x] == x)
return x;
return Find(parent[x]);
}
黑吃黑
bool Same(int x, int y){
return Find(x)==Find(y);
}
void Union(int x, int y){
parent[Find(x)] = Find(y);
}
時間複雜度
- 由於所有操作都是由Find構成,只需要分析Find的時間
- 但很可惜Find最差會是\(O(n)\)
優化1
啟發式合併
紀錄高度
- 老大紀錄整棵樹的高度
- Union時把小的接到大的下面
3
2
紀錄高度
- 老大紀錄整棵樹的高度
- Union時把小的接到大的下面
3
紀錄高度
- 兩棵樹高度一樣時
- Union後高度才會增加
2
2
3
紀錄高度
int parent[1000005];
int h[1000005];
void init(int n){
for(int i = 1; i <= n ++i){
parent[i] = i;
h[i] = 1;
}
}
int Find(int x); // 和之前一樣
void Union(int x, int y){
x = Find(x);
y = Find(y);
if(h[y] < h[x])
swap(x,y);
parent[x] = y;
if(h[x] == h[y])
++h[y];
}
時間複雜度
- 兩棵樹高度一樣時
Union後高度才會增加 - 所以一顆高度為\(k\)的樹
至少會有\(2^{k-1}\)個點 - 所以樹高為\(O(\log n)\),同時也是Find的時間複雜度
紀錄Size
- 每個老大紀錄整棵樹的節點數量(Size)
- 能得到一樣的時間複雜度
- 且Size比高度有用
2
3
5
紀錄Size
int parent[1000005];
int sz[1000005];
void init(int n){
for(int i = 1; i <= n ++i){
parent[i] = i;
sz[i] = 1;
}
}
int Find(int x); // 和之前一樣
void Union(int x, int y){
x = Find(x);
y = Find(y);
if(sz[y] < sz[x])
swap(x,y);
parent[x] = y;
sz[y] += sz[x];
}
優化2
路徑壓縮
路徑壓縮
- Find的過程中
走過的點他們的老大都是同一個人 - 那就乾脆直接指過去
R
R
路徑壓縮
int parent[1000005];
int Find(int x){
if(parent[x] == x)
return x;
parent[x] = Find(parent[x]);
return parent[x];
}
時間複雜度
- 假設Find總共執行\(f\)次
- \(\Theta(n+f\cdot(1+\log_{2+f/n} n))\)
- 平均每次也是\(O(\log n)\)
一般比賽用這個就足夠了
優化3
兩個優化一起做
兩個優化
- 優化1只會動到Union
- 優化2只會動到Find
- 兩個一起做會發生甚麼事?
Find複雜度
\(O(\alpha(n))\)
\(\alpha(n)\)是反阿克曼函數,基本上等於常數!
練習題
POJ 1182
延伸
啟發式合併
啟發式合併
- 假設種共有\(n\)個元素
分別被放在\(m\)個資料結構\(T_1,...T_m\)中 - 這些資料結構可以做合併的操作
\(T_i.union(T_j)\)表示將\(T_j\)中所有元素丟到\(T_i\)中
Disjoint Set
By jacky860226
Disjoint Set
- 205