并查集

并查集的原理介绍

并查集是一种特殊的集合,它包括“并”和“查”两部分,就是说,它只能进行“并”和“查”两种操做。ios

举个形象的例子来体现并查集:

小明在玩一个叫作“到底有几个团队”的游戏,这个游戏是这样的:有若干我的组成了若干个团队,而后,这些人会给小明若干个线索,线索相似于说“xx和xx在一个团队里”,小明要作的,就是根据线索求出到底有多少个团队。数组

好比说:优化

如今有\(3\)我的,分别是小力,小华和小刚。spa

这时他们给出了\(1\)条线索:小华和小刚是一个团队的。3d

很显然,有\(2\)个团队。code

但若是再复杂一点呢?blog

如今有\(10\)我的,分别是\(1,2,3,4,5,6,7,8,9,10\)递归

这时他们给出了\(7\)条线索:游戏

\(2\)\(4\)是一个团队的;ci

\(5\)\(7\)是一个团队的;

\(1\)\(3\)是一个团队的;

\(8\)\(9\)是一个团队的;

\(1\)\(2\)是一个团队的;

\(5\)\(6\)是一个团队的;

\(2\)\(3\)是一个团队的。

是否是感受有点晕了,这么多人和线索,并且这些线索绕来绕去的,实在有些难分辨。

固然,咱们能够画图来帮助解题。

最初始的状态:

获得线索“\(2\)\(4\)是一个团队的”时:

获得线索“\(5\)\(7\)是一个团队的”时:

获得线索“\(1\)\(3\)是一个团队的”时:

获得线索“\(8\)\(9\)是一个团队的”时:

获得线索“\(1\)\(2\)是一个团队的”时:

获得线索“\(5\)\(6\)是一个团队的”时:

获得线索“\(2\)\(3\)是一个团队的”时:

由于\(2\)\(3\)在获得线索“\(1\)\(3\)是一个团队的”时就在一个团队里了,因此状态没有发生变化。

从图中可知,共有\(4\)个团队。

并查集就相似于模仿上文“画图”的方式来进行问题求解。

并查集的代码实现

上文说到,并查集的操做分为“并”和“查”两部分,咱们来说讲并查集的两种操做的实现。

“并”

void unionn(int x,int y)//"并"
{
    x=find(x);
    y=find(y);
    if(x!=y)father[y]=x;
}

“查”

递归实现

int find(int x)//"查"
{
    if(father[x]!=x)return find(father[x]);
    else return x;
}

非递归实现

int find(int x)//"查"
{
    while(father[x]!=x)x=father[x];
    return x;
}

并查集的路径压缩

当题目数据比较特殊,好比是一条链时,这种“并”与“查”的方式就会超时。这时就要用到一种优化的方法:路径压缩。这种作法就是在找完某个元素的根节点以后,在递归回来的时候顺便把路径上元素的父亲都指向根节点。

举个例子:

没有路径压缩的元素存储方式:

有路径压缩的元素存储方式:

从图中能够看出来,有路径压缩的并查集进行“查”的操做会更快。

实现以下(仅能递归实现)

int find(int x)//"查"
{
    if(father[x]!=x)father[x]=find(father[x]);
    return father[x];
}

并查集的初始化

注意,最初始的状态是全部元素的父亲就是本身,因此,应当进行初始化。
代码以下:

for(int i=1;i<=n;i++)
    father[i]=i;

实现例子

看了那么久,咱们来作一道题目吧,将例子中的人数设定为\(n(n \le 10000)\)人,将线索设定为\(m(m \le 10000)\)条,线索的输入方式设定为:A B\((A,B\)为数字\()\)如今来作一作吧。

\(Code:\)

#include <iostream>
using namespace std;
int father[10010];//father数组存储着并查集元素 
void start(int n)//初始化 
{
    for(int i=1;i<=n;i++)
        father[i]=i;
}
int find(int x)//"查"
{
    if(father[x]!=x)father[x]=find(father[x]);
    return father[x];
}
void unionn(int x,int y)//"并"
{
    x=find(x);
    y=find(y);
    father[x]=y;
}
int main()
{
    int n,m,ans=0;
    cin>>n>>m;
    start(n);
    for(int i=1;i<=m;i++)
    {
        int a,b;
        cin>>a>>b;
        unionn(a,b);//将a,b合并成一个团队 
    }
    for(int i=1;i<=n;i++)
        if(father[i]==i)ans++;//若是某个元素的父亲就是它本身,则这是一个以其为首的团队 
    cout<<ans;
    return 0;
}
相关文章
相关标签/搜索