并查集
并查集主要包括初始化、寻根以及合并三个操作。
例如a、b、c、d、e,初始化他们的祖先为本身。
寻根操作:
int find_root(int x){//非路径压缩 return x==s[x]?x:finde_root(s[x]); }
上述寻根操作,没有对路径进行压缩。具体表现为如果a的祖先是b,b的祖先是c,c的祖先是d。那么在对a进行寻根的时候会查找三次,即树太高了
int find_root(int x){//路径压缩 if(x!=s[x]) s[x]=find_root(s[x]); return s[x]; }
给出非递归版本
int find_root(int x){ int r=x; while(r!=s[r]) r=s[r];//找到x最大的祖先 int i=x; int j; while(i!=r){ j=s[i];//先把i此时的祖先存好 s[i]=r;//然后把此时的祖先的祖先设为最终的祖先即r i=j;//i变为上面的一个祖先 } return r; }
使用了路径压缩之后,相当于a,b,c的祖先直接就是d,那么只需要查询一次即可,使用路径压缩的时候,我们每次寻根的时候都需要调用find_root,这样是为了路径能一直更新。
合并操作:
void merge_set(int x,int y){ x=find_root(x); y=find_root(y); if(x!=y){ s[x]=y; } }
合并之前我们调用了find_root函数找到x和y的祖先,按理来说既然已经使用了路径压缩,那么直接使用s[x]和s[y]也是可以达到找到祖先的效果,但是要考虑到一点,如果此时路径还没压缩,那么直接找找不到祖先节点。
例子:洛谷P3367
题目描述
如题,现在有一个并查集,你需要完成合并和查询操作。
输入格式
第一行包含两个整数 N,M ,表示共有 N 个元素和 M 个操作。
接下来 M 行,每行包含三个整数 Zi,Xi,Yi 。
当 Zi=1 时,将 Xi 与 Yi 所在的集合合并。
当 Zi=2 时,输出 Xi 与 Yi 是否在同一集合内,是的输出 Y
;否则输出 N
。
输出格式
对于每一个 Zi=2 的操作,都有一行输出,每行包含一个大写字母,为 Y
或者 N
。
#include<bits/stdc++.h> using namespace std; const int MAXN=10004; int s[MAXN]; int find_root(int x){ if(x!=s[x]) s[x]=find_root(s[x]); return s[x]; } void merge_set(int x,int y){ x=find_root(x); y=find_root(y); if(x!=y){ s[x]=y; } } void isOneSet(int x,int y){ x=find_root(x); y=find_root(y); if(s[x]!=s[y]){ cout<<'N'<<endl; }else cout<<'Y'<<endl; } void init_set(int N){ for(int i=0;i<N;i++){ s[i]=i; } } int main() { int N,M,x,y,z; cin>>N>>M; init_set(N); for(int i=0;i<M;i++){ cin>>z>>x>>y; if(z==1){ merge_set(x,y); }else{ isOneSet(x,y); } } return 0; }
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!