學習並查集前提須知
並查集支持合並與查詢,針對於查詢某兩點是否在同一個樹內,或者將兩點之間連一條線。
算法內容
競賽需要用到的點
1、並查集多用於其他算法的過渡使用,不單獨考
2、並查集的思路會多次在以后出現,請理解並查集的每一步思路
並查集略講
並查集是一個很簡單的數據結構,其基本思路圍繞着一個就是根節點展開,若有兩點的根節點相同那么就肯定在一棵樹內,所以我們只需要維護一個點的父親節點就好了,然后每次詢問都查找根節點是否相同。
但這是一種最優的情況,若樹退化成鏈的話,很明顯,復雜度高了很多,那就和暴力相同,怎樣避免這種情況發生呢?我們上面說到,我們只需要判斷兩點和根節點的關系,那么我們只保留當前點的根節點關系不就好了?的確,這樣就能夠滿足我們的問題了,這樣的優化方式我們叫做路徑壓縮,且這樣就出現了圖論中的一種特殊圖 菊花圖,這種圖你以后會遇到,多用於卡你的算法,在這里這個不是重點,不再贅述。
那么根據上面的信息我們就可以開始寫代碼了
部分代碼展現
起始化
//#define fre yes
#include <cstdio>
const int N = 1000005;
int par[N], Rank[N]; //Rank表示的是秩優化
void init(int n) {
for (int i = 0; i <= n; i++) {
par[i] = i;
Rank[i] = 1;
}
}
int main() {
static int n, m;
...
init(n);
...
}
合並操作 + 路徑壓縮 + 秩優化
int find(int x) {
if(x == par[x]) return x;
else return par[x] = find(par[x]);
} //路徑壓縮(與返回父親節點)
void unite(int x, int y) {
x = find(x);
y = find(y);
if(x == y) return ;
if(Rank[x] <= Rank[y]) par[x] = y;
else par[y] = x;
if(Rank[x] == Rank[y]) Rank[x]++;
} //秩優化 與 合並操作
查詢操作
int find(int x) {
if(x == par[x]) return x;
else return par[x] = find(par[x]);
}
bool same(int x, int y) {
return find(x) == find(y);
}
所有代碼
//#define fre yes
#include <cstdio>
const int N = 200005;
int par[N], Rank[N];
void init(int n) {
for (int i = 0; i <= n; i++) {
par[i] = i;
Rank[i] = 1;
}
}
int find(int x) {
if(par[x] == x) return x;
else return par[x] = find(par[x]);
}
void unite(int x, int y) {
x = find(x);
y = find(y);
if(x == y) return ;
if(Rank[x] <= Rank[y]) par[x] = y;
else par[y] = x;
if(Rank[x] == Rank[y]) Rank[x]++;
}
bool same(int x, int y) {
return find(x) == find(y);
}
int main() {
static int n, m;
scanf("%d %d", &n, &m);
init(n);
for (int i = 1; i <= m; i++) {
int k;
scanf("%d", &k);
if(k == 1) {
int x, y;
scanf("%d %d", &x, &y);
unite(x, y);
} else {
int x, y;
scanf("%d %d", &x, &y);
if(same(x, y)) puts("Y");
else puts("N");
}
} return 0;
}