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

1|並查集

並查集


並查集是一種樹形資料結構,用於處理一些不相交集合的合併及查詢問題。
常見的用途有求連通子圖、求最小生成樹的Kruskal演算法和求最近公共祖先(LCA)等。
建立並查集只需要三個步驟。

演算法步驟

  1. 初始化。把每個點所在集合初始化為其自身。
  2. 查詢。查詢兩個元素所在的集合,即找祖宗。
  3. 合併。如果兩個元素的集合號不同,將兩個元素合併為一個集合。

注意

  1. 查詢時,遞迴找祖宗,祖宗集合號等於本身時停止。迴歸時,把查詢路徑上的所有節點統一為祖宗的集合號。
  2. 合併時,只需要把一個元素的祖宗集合號改為另一個元素的祖宗集合號。“擒賊先擒王”,只改祖宗即可!。這是一個路徑壓縮的過程。

一個簡單的小例子

#include <stdio.h>

#define MAXN 255
int fa[MAXN];
/*用fa[]來表示集合號*/
void init(int n){ //初始化
	for (int i = 1; i <= n; i++) {
		fa[i] = i;
	}
}
int find(int i) {			  //遞迴找祖宗
	if (i == fa[i])
		return i;
	else {
		fa[i] = find(fa[i]);  //該步進行了路徑壓縮
		return fa[i];		  //返回父節點
	}
}
void unionn(int a, int b) { //合併集合
	int a_fa = find(a);  // 找a 的祖宗p
	int b_fa = find(b);  // 找b 的祖宗q
	fa[a_fa] = b_fa;	 // a的祖先指向b的祖先
}
using namespace std;
int main() {
	int n, m, x, y, q;
	scanf("%d", &n);	//一共有n個人
	init(n);
	scanf("%d", &m);	//一共有m組關係
	for (int i = 1; i <= m; i++) {
		scanf("%d %d", &x, &y);
		unionn(x, y);
	}
	scanf("%d", &q);
	for (int i = 1; i <= q; i++) {
		scanf("%d %d", &x,&y);
		if (find(x) == find(y))
			printf("YES\n");
		else
			printf("NO\n");
	}
	return 0;
}

演算法複雜度