並查集筆記
阿新 • • 發佈:2022-03-30
基礎知識
基本操作
// 初始化
void init(){
for(int i = 1; i <= n; i++)
p[i] = i;
}
// 查詢(路徑壓縮)
int find(int x){
return x == p[x] ? x : (p[x] = find(p[x]));
}
// 普通合併
void union(int x, int y){
int fx = find(x), fy = find(y);
if(fx != fy) p[fx] = fy;
}
啟發式合併
- 由於合併時希望操作元素儘量少,就讓少的往大的合併,這就是啟發式合併
-
\(n\) 個元素和 \(m\)
// 啟發式合併
void union(int x, int y){
int fx = find(x), fy = find(y);
if(fx == fy) return;
if(sz[fx] > sz[fy])
swap(fx, fy);
p[fx] = fy;
sz[fy] += sz[fx];
}
按深度合併
- 每次合併將深度小的一方合併到深度大的一方
- 路經壓縮時,可能破壞深度值,複雜度不變差
// 按深度合併 void union(int x, int y){ int fx = find(x), fy = find(y); if(fx == fy) return; if(dep[fx] > dep[fy]) swap(fx, fy); p[fx] = fy; if(dep[fx] == dep[fy]) // 只有深度相等才更新 dep[fy]++; }
時間複雜度
- 啟發式合併和深度合併,\(n\) 個元素和 \(m\) 次查詢,時間複雜度為 \(O(mlogn)\)
- 一般來說並查集時間複雜度為 \(O(m*\alpha (m, n))\)。其中 \(\alpha\) 為阿克曼函式的反函式,可以認為是一個小常數
- 無啟發式合併,只路徑壓縮最壞時間複雜度為 \(O(mlogn)\),平均複雜度為 \(O*\alpha(m,n)\)。
- 可以直接認為 \(O(m)\)