1. 程式人生 > 其它 >並查集筆記

並查集筆記

基礎知識

基本操作

// 初始化
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\)
    次查詢,時間複雜度為 \(O(mlogn)\)
// 啟發式合併
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)\)