并查集 带权并查集 反集
并查集
并查集最基本的操作——合并、查询
struct{
int p,r;//p:parent r:rank(秩,子树高度的下界)
}n[MAX];
void init(){//初始化并查集:每个元素都是自身的根节点,秩为0
for(int i=1;i<=MAX;i++)
n[i].p=i,n[i].r=0;
}
- 查:查找x所属的子树(路径压缩:将每个节点的父节点更新为其祖先)
int find(int x){
if(x!=a[x].parent)//x不是根节点,继续向上找,直至找到祖先(祖先的父节点为其自己)
a[x].parent=find(a[x].parent);
return a[x].parent;
}
- 并:合并x和y节点(按秩合并/启发式合并,秩小的合并到秩大的上)
void merge(int x,int y){
int rx=find(x),ry=find(y);
if(rx!=ry){//x和y不属于相同树上的节点,进行合并
if(a[rx].rank>a[ry].rank)
a[ry].parent=rx;
else{
a[rx].parent=ry;
if(a[rx].rank==a[ry].rank) a[ry].rank++;//秩相等则合并后秩+1
}
}
}
带权并查集
节点与其父节点间存在权值,以权值相加为例
struct{
int p,r,w;//w:记录节点与其父节点间的权值
}n[MAX];
void init(){//需将权值也初始化为0
for(int i=1;i<=MAX;i++)
n[i].p=i,n[i].r=0,n[i].w=0;
}
- 查:节点权值与其原父节点权值层层相加,通过递归实现权值相加到新父节点
int find(int x){
if(n[x].p!=x){
int t=n[x].p;//保存原父节点
n[x].p=find(node[x].p);
n[x].w+=n[t].w;//经过递归原父节点权值已经改变,节点权值加上原父节点权值就行了
}
return n[x].p;
}
- 并:具体问题具体分析
void merge(int x,int y,int w) {//w:x和y间的权值差异
int rx=find(x),ry=find(y);
if(rx!=ry){
if(node[rx].r<node[ry].r){
node[rx].p=ry;
node[rx].w=w+node[y].w-node[x].w;
}else{
node[ry].r=rx;
node[ry].w=-w+node[x].w-node[y].w;
if(node[rx].r==node[ry].r) node[rx].r++;
}
}
}
种类并查集(反集)
集合中同时记录2种性质的信息:朋友和敌人。
实现:开2倍长的数组(2*n), 1 − n 1-n 1−n表示元素 1 − n 1-n 1−n一一对应的朋友节点集合的根节点,但朋友集合元素间不一定为朋友; n − 2 n n-2n n−2n为元素 1 − n 1-n 1−n一一对应的敌人集合的根节点,但敌人集合元素间不一定为敌人。
例:对于元素a:a:指其朋友集合中的根节点,即为自己;a+n:指其所对应的敌人集合的根节点(如:n=4,则1和5为敌对关系,以此类推)
合并:每次进行合并操作的都是朋友关系。如a和b如果是朋友,则merge(a,b)。
为维护敌对关系,如a和b是敌人,则将b的敌人与a合并(merge(b+n,a)),a的敌人与b合并(merge(a+n,b)),合并结果为b的敌人与a成为朋友(即a成为b的敌人),a的敌人与b成为朋友(即b成为了a的敌人)。
注意:元素 n − 2 n n-2n n−2n所构成的敌人集合,只是为了维护敌对关系而虚构出来的集合,实际上并不另外存在n个敌人。因此维护敌对关系时,并不能直接操作敌人集合,而是必须通过操作朋友集合来间接操作敌人集合。(即进行合并时**一定要注意合并顺序!**只能向朋友集合上合并,不可以向敌对集合上合并!必须将a+n的父节点设为b,b+n的父节点设为a!)
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· winform 绘制太阳,地球,月球 运作规律
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· AI 智能体引爆开源社区「GitHub 热点速览」
· Manus的开源复刻OpenManus初探
· 写一个简单的SQL生成工具