并查集

博客食用更佳bossbaby's blog

并查集,在一些有N个元素的集合应用问题中,我们通常是在开始时让每个元素构成一个单元素的集合,然后按一定顺序将属于同一组的元素所在的集合合并,其间要反复查找一个元素在哪个集合中。
引自 百度百科

例题引入

现在有n个人,每次会告诉我们哪两个人有关系,最后求两个人有没有关系。
根据暴力出奇迹的原则,我们可以建一个图,当有两个人有关系时,我们就对他们连一条线,查询时就dfs一下看是否联通
不过这样复杂度无论时间还是空间都太大了。
然后,并查集就上场了

讲解

并查集用来维护元素集合的连通性与集合个数
并查集由两部分组成:每个元素以及它的父亲,相同父亲的元素就是在同一个集合中
假设每个元素存放数组a中,每个元素的父亲存放在数组fa中,它是每个元素的父亲在a数组中的下标
它初始时就是这个样子的,每个元素的父亲都是自己:
a 1 2 3 4 …
fa 1 2 3 4 …
然后,每当有两个元素有关系时,就把其中一个元素的父亲指向另一个.
这样每次我们要找到他的最终父亲是谁时,就可以找到他的父亲,再找到他父亲的父亲。最后当找到一个父亲是自己的元素时,我们就知道它就是我们要寻找的最终父亲了。为了下次寻找方便,我们把一路经过的所有点的父亲都修改成最终的父亲(这就是路压,路径压缩,后面会有,不是必要的,但可以加快速度,建议使用).
假设我们知道2和4有关系
然后我们的数组就变成了这样:
a 1 2 3 4 …
fa 1 2 3 2 …
把2的fa变成4还是把4的变成2是随机的,而且对结果不影响。
但有些题是有影响的,所以在合并时必须要判断
最后,上代码
unionn函数:把a,b合并到一个集合
find函数:找到并返回x的父亲,有带路压,不带路压两种

int find(int x){//带路压
     if(fa[x]!=x)//不是最终父亲,即父亲是自己的元素
         fa[x]=find(fa[x])//递归更新,路径压缩。。。这里特别复杂,建议没有学过的人好好理解
     return fa[x];
}

int find(int x){//不带路压
    while(fa[x]!=x)
        x=fa[x];
    return x;
}

void unionn(int x,int y){//合并函数,带路压
    int r1=find(x),r2=find(y);//找到xy的父亲,把其中一个父亲的父亲设置为另一个的父亲。
    fa[r2]=r1;
    find(y);//路径压缩,把y的每个父亲及其父亲的父亲设置为改变后的父亲。
}

void unionn(int x,int y){//合并函数,不带路压
    int r1=find(x),r2=find(y);//找到xy的父亲,把其中一个父亲的父亲设置为另一个的父亲。
    fa[r2]=r1;//不带路压
}
posted @ 2019-05-30 20:34  bossbaby  阅读(135)  评论(0编辑  收藏  举报