?所謂并查集就是可以畫圖理解
假如說(shuō)我們想要構(gòu)建一個(gè)樹(也是圖),要求1->2,2->4,1->3
在構(gòu)另一個(gè)樹,要求5->6,6->7,5->8
1是2的頭結(jié)點(diǎn),2是4的頭結(jié)點(diǎn),以此類推
下面要求去將5連接到1上,就是我下面要講的,其實(shí)上面的子節(jié)點(diǎn)的連接也是如此的。
簡(jiǎn)單并查集例題:
一共有 n 個(gè)數(shù),編號(hào)是 1~n,最開始每個(gè)數(shù)各自在一個(gè)集合中。
現(xiàn)在要進(jìn)行 m 個(gè)操作,操作共有兩種:
- M a b,將編號(hào)為 a 和 b 的兩個(gè)數(shù)所在的集合合并,如果兩個(gè)數(shù)已經(jīng)在同一個(gè)集合中,則忽略這個(gè)操作;
- Q a b,詢問(wèn)編號(hào)為 a 和 b 的兩個(gè)數(shù)是否在同一個(gè)集合中;
輸入格式
第一行輸入整數(shù) n 和 m。
接下來(lái) m 行,每行包含一個(gè)操作指令,指令為 M a b 或 Q a b 中的一種。
輸出格式
對(duì)于每個(gè)詢問(wèn)指令 Q a b,都要輸出一個(gè)結(jié)果,如果 a 和 b 在同一集合內(nèi),則輸出 Yes,否則輸出 No。
每個(gè)結(jié)果占一行。
分析一下:
面對(duì)這種比較新的數(shù)據(jù)結(jié)構(gòu)一般都是非常抽象的,但是一旦通過(guò)畫圖或者推理理解了,也就很容易記住了,首先我們將p[i]=i,為的是存儲(chǔ)此樹的頭結(jié)點(diǎn),接下來(lái)要進(jìn)行連接的操作,就要通過(guò)find(),壓縮一下路徑。將子節(jié)點(diǎn)連接到p[b]=find(p[a])(a為子節(jié)點(diǎn),b為父節(jié)點(diǎn)),下面都是這種操作。
然后這個(gè)題目他讓判斷是不是在一個(gè)集合就可以find(a)==find(b)來(lái)判斷是不是頭結(jié)點(diǎn)一樣,因?yàn)樽罱Kfind(a)返回的是頭結(jié)點(diǎn)的值。
代碼實(shí)現(xiàn):
#include<bits/stdc++.h>
using namespace std;
const int N=1e5+10;
int p[N];
int find(int x){//返回x的祖宗節(jié)點(diǎn)+路徑壓縮
if(p[x]!=x)p[x]=find(p[x]);//只有祖宗節(jié)點(diǎn)才有p[x]=x
return p[x];
}
int n,m;
signed main(){
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++)p[i]=i;
for(int i=1;i<=m;i++){
char op[2];//讀字符串比讀字符更省事
int a,b;
scanf("%s%d%d",op,&a,&b);
if(*op=='M')p[find(a)]=find(b);
else {
if(find(a)==find(b))printf("Yes\n");
else printf("No\n");
}
}
}
下面還有一類題目:讓求一個(gè)樹里面有多少子節(jié)點(diǎn)
給定一個(gè)包含?n?個(gè)點(diǎn)(編號(hào)為?1~n)的無(wú)向圖,初始時(shí)圖中沒(méi)有邊。
現(xiàn)在要進(jìn)行?m?個(gè)操作,操作共有三種:
-
C a b
,在點(diǎn)?a?和點(diǎn)?b?之間連一條邊,a?和?b?可能相等; -
Q1 a b
,詢問(wèn)點(diǎn)?a?和點(diǎn)?b?是否在同一個(gè)連通塊中,a?和?b?可能相等; -
Q2 a
,詢問(wèn)點(diǎn)?a?所在連通塊中點(diǎn)的數(shù)量;
輸入格式
第一行輸入整數(shù)?n?和?m。
接下來(lái)?m?行,每行包含一個(gè)操作指令,指令為?C a b
,Q1 a b
?或?Q2 a
?中的一種。
輸出格式
對(duì)于每個(gè)詢問(wèn)指令?Q1 a b
,如果?a和?b在同一個(gè)連通塊中,則輸出?Yes
,否則輸出?No
。
對(duì)于每個(gè)詢問(wèn)指令?Q2 a
,輸出一個(gè)整數(shù)表示點(diǎn)?a 所在連通塊中點(diǎn)的數(shù)量
每個(gè)結(jié)果占一行。
數(shù)據(jù)范圍
1≤n,m≤10^5
輸入樣例:
5 5
C 1 2
Q1 1 2
Q2 1
C 2 5
Q2 5
輸出樣例:
Yes
2
3
分析過(guò)程:
?這個(gè)題的求節(jié)點(diǎn)數(shù),只用拿個(gè)數(shù)組將所有的子節(jié)點(diǎn)連接過(guò)程一一地加到父節(jié)點(diǎn)的si[b](b為父節(jié)點(diǎn)),最后輸出的是si[find(a)](a為樹中任意一個(gè)數(shù))。這樣我們就求到了樹的節(jié)點(diǎn)數(shù)。
但是別忘記初始化為si[i]=1,不是si[i]=i文章來(lái)源:http://www.zghlxwxcb.cn/news/detail-801040.html
代碼實(shí)現(xiàn):
#include<bits/stdc++.h>
#define read(x) scanf("%d",&x)
using namespace std;
const int N = 1e5+5;
int n,m,a,b,fa[N], si[N];
string act;
void init() {//初始化
for (int i=1; i<=n; i++) {
fa[i] = i;
si[i] = 1;
}
}
int find(int x) {//查找父節(jié)點(diǎn)
if(fa[x]==x) return x;
else return fa[x] = find(fa[x]);
}
void merge(int a,int b) {//節(jié)點(diǎn)數(shù)加起來(lái)
int x = find(a);
int y = find(b);
fa[x] = y;
si[y] += si[x];
}
bool ask(int a,int b) {//詢問(wèn)是不是頭結(jié)點(diǎn)一樣
return find(a)==find(b);
}
int main() {
read(n),read(m);
init();
while(m--) {
cin>>act;
if(act=="C") {
read(a),read(b);
if(!ask(a,b)) merge(a,b);
} else if(act=="Q1") {
read(a),read(b);
ask(a,b) ? printf("Yes\n") : printf("No\n");
} else {
read(a);
printf("%d\n",si[find(a)]);
}
}
return 0;
}
以上就是并查集這一種數(shù)據(jù)結(jié)構(gòu)的代碼思路和方法了。文章來(lái)源地址http://www.zghlxwxcb.cn/news/detail-801040.html
到了這里,關(guān)于圖論:并查集的合并、判斷和求節(jié)點(diǎn)的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!