并查集

一 ,并查集的构造

① 设定一个集合,叫并查集,即 Disjoint Set,功能是检查 图中 是否出现了 环
② 往集合里面添加边,怎么添加呢。取边的起点和终点,判断两点是否都在集合里面,如果都在,则出现了环,如果不在,将两个点 放入集合中。
③ 继续添加下一条边,如果没有边了,还没有找到环,就是没有环了

二,数组以树的形式实现

上面所讲的就是基本的思想了,下面讲如何具体实现

① 由于集合操作并不方便,所以便设法使用数组实现

② 由于上面所说的添加边 可以看成是一组点的集合 连接 另外一组点的集合,所以我们只要随便让集合的某一点 连接 另外一个集合的任意一点 就行了
这样就可以让所有点处于连通状态

③ 基于上述所讲,我们可以将点以树的形式连接,为什么是树呢?因为集合中没有环,所以最后画出来的图形就是树了。

④ 由于连接的随意性, 这种连接方式,并不能正确表示一张图,只是能表示所有点是连接在一起的,且这种图画出来是树的形式。

⑤ 所以,我们可以构造一个 parent 数组,什么意思呢 ? 就是 parent[x]=y, 指 x 的父节点是 y ,

为什么要 定义一个父节点呢? 定义父节点的意思是 你可以通过父节点找到这个集合的根 ,根就是 所有点 都由 这个点 出发得到的。

那么,为什么要找到这个根呢? 即这样做的优点。
优点1,在添加边的时候,可以通过查找 边上两个点的根结点是否存在,是否相同,达到判断两个点是否在之前的集合中出现过的效果
优点2,可以在 加边 时减少树的深度,之后查找 根节点时就可以减少步骤

三,压缩路径

且看我上面优点 2 这一句 “但如果让 3 的父节点为 0 深度增加了一层” ,其实如果但如果让 0 的父节点为 3 深度就没有增加了

之前的代码中,我们这一步那个时那个的父节点我们并没有对此做一个判断,来判断哪一个成为 父节点 树的深度会更少。
这样子就有一个问题,如果一直是
0-1 1-2 2-3 3-4 这样子就会让树的深度一直增加,这样就会让 find_root 函数时间复杂度在最坏的情况下达到 o(n) ,即每个点都要查找。
所以,我们使用 rank 数组来记录 树的深度,如 rank[x]=y 表示 以 x 点为根结点的话,树的深度为 y
题目:
https://www.luogu.com.cn/problem/P1551

#include
using namespace std;
int a[5001],rank[5001];
int n,m,p;
//n个人,m个亲戚关系,询问p对亲戚关系 
//寻找该点的最最最原始的结点,直到找到没有父节点的0为止 
int find_root(int x,int a[]){
    //先将根节点设为自己 
    int x_root = x;
    //往上找,如果不是0就继续 
    while(a[x_root]){
        x_root = a[x_root];
    }
    //返回根节点 
    return x_root;
}
//合并 
int union_vertices(int x,int y,int a[],int rank[]){
    //找到x和y的根节点 
    int x_root = find_root(x,a);
    int y_root = find_root(y,a);
    //如果相同就不用并了,是一家人 
    if(x_root == y_root){
        return 0;
    }else{
        //a[x_root]=y_root;
        //如果x的层数比y的层数高,就把y并到x上,总层数不变 
        if(rank[x_root] > rank[y_root]){
            a[y_root] = x_root;
        }else if(rank[y_root] > rank[x_root]){
            a[x_root] = y_root;
        }else{//如果层数相同,就随便并一下,层数加一 
            a[x_root] = y_root;
            rank[y_root]++; 
        }   
        return 1;
    }   
}

int main(){
    cin>>n>>m>>p;
    while(m--){
        int x,y;
        cin>>x>>y;
        union_vertices(x,y,a,rank);
    }
    while(p--){
        int x,y;
        cin>>x>>y;
        int x_root = find_root(x,a);
        int y_root = find_root(y,a);
        if(x_root == y_root){
            cout<<"Yes"<

链接:
https://www.cnblogs.com/asdfknjhu/p/12515480.html
https://www.bilibili.com/video/BV13t411v7Fs

你可能感兴趣的:(并查集)