并查集|C++

本文介绍了并查集的基本原理,包括FIND和UNION操作,以及两种优化策略:按秩合并和路径压缩。并提供了C++实现的代码示例。此外,文章通过一个亲戚关系问题的实例展示了并查集如何解决此类问题,给出了AC代码,帮助读者理解并查集的实际运用。
摘要由CSDN通过智能技术生成

1.并查集原理

并查集算法笔记

1.1针对问题

  • FIND:判断两个节点是否在同一集合中
  • UNION:归并两个集合
  • 并查集可以很容易解决等价类问题,通过FIND可以检查一个等价对的两个元素是否在同一棵树中,如果是,由于它们已经在同一个等价类中,就不需要变动;否则就可以用UNION函数归并两个等价类。

1.2版本

1.2.1最简单版本示例

#include<bits/stdc++.h>
using namespace std;

#define MAXN 1000
int father[MAXN];
//初始化
void init(int n){
    for(int i=0;i<n;i++){
        father[i]=-1;
    }
}
//查询
int FIND(int x){//x:查询位置
    if(father[x]==-1)
        return x;
    else 
        return find(father[x]);
}
//归并
void UNION(int i,int j){
    father[i]=j;
}

1.2.2加权合并规则(按秩合并)

#include<bits/stdc++.h>
using namespace std;

#define MAXN 1000
int father[MAXN];
int Rank[MAXN];
//初始化
void init(int n){
    for(int i=0;i<n;i++){
        father[i]=-1;
        Rank[i]=1;
    }
}
//查询
int FIND(int x){//x:查询位置
    if(father[x]==-1)
        return x;
    else 
        return FIND(father[x]);
}
//归并
void UNION(int i,int j){
    if(Rank[i]<=Rank[j])
        father[i]=j;
    else
        father[j]=i;
    if(Rank[i]==Rank[j]&&i!=j){
        Rank[j]++;
    }
}

1.2.3路径压缩

#include<bits/stdc++.h>
using namespace std;

#define MAXN 1000
int father[MAXN];
int Rank[MAXN];
//初始化
void init(int n){
    for(int i=0;i<n;i++){
        father[i]=-1;
        Rank[i]=1;
    }
}
//查询
int FIND(int x){//x:查询位置
    if(father[x]==-1)
        return x;
    father[x]=FIND(father[x]);//父节点设为根节点
    return father[x];//返回父节点
}

//归并
void UNION(int i,int j){
    father[i]=j;
}

2.例题

2.1亲戚问题

题目背景
若某个家族人员过于庞大,要判断两个是否是亲戚,确实还很不容易,现在给出某个亲戚关系图,求任意给出的两个人是否具有亲戚关系。
题目描述
规定:x和y是亲戚,y和z是亲戚,那么x和z也是亲戚。如果x,y是亲戚,那么x的亲戚都是y的亲戚,y的亲戚也都是x的亲戚。
输入格式
第一行:三个整数n,m,p,(n<=5000,m<=5000,p<=5000),分别表示有n个人,m个亲戚关系,询问p对亲戚关系。
以下m行:每行两个数Mi,Mj,1<=Mi,Mj<=N,表示Mi和Mj具有亲戚关系。
接下来p行:每行两个数Pi,Pj,询问Pi和Pj是否具有亲戚关系。
输出格式
P行,每行一个’Yes’或’No’。表示第i个询问的答案为“具有”或“不具有”亲戚关系。

AC代码:

#include<iostream>
using namespace std;

#define MAXN 5000
int Father[MAXN];
int Rank[MAXN];

void init(int n){
    for(int i=0;i<n;i++)
    {
        Father[i]=-1;
        Rank[i]=1;
    }
}

int find(int c){
    if(Father[c]==-1)
        return c;
    else{
        return find(Father[c]);
    }
}

void Union(int x,int y){
    int m=Rank[x];
    int n=Rank[y];
    if(m<=n){
        Father[x]=y;
    }
    else
        Father[y]=x;
    if(m==n&&x!=y){
        Rank[y]++;
    }
}

int main(){
    int n,m,p;
    init(n);
    cin>>n>>m>>p;
    for(int i=0;i<m;i++){
        int mi,mj;
        cin>>mi>>mj;
        Union(mi,mj);
    }
    for(int i=0;i<p;i++){
        int pi,pj;
        cin>>pi>>pj;
        if(find(pi)==find(pj))
            cout<<"Yes"<<endl;
        else
            cout<<"No"<<endl;
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值