并查集

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了并查集相关的知识,希望对你有一定的参考价值。

并查集算法多用于复杂关系的计算,比如亲戚的计算(计算总共有多少组亲戚关系,亲戚的亲戚也是亲戚),一个公司有多少组(老大的老大也是老大)。

 

算法要点:

1、用树(森林)存放关系,有关系的人一定有一个相同的根节点。

2、用数组存放节点关系,如tree【2】 = 3,表示2号人的根节点是3(2的老大是3)。

3、X和Y有关系的话,X的根节点更新为Y的根节点(与XY的顺序无关的)。注意是更新成Y的根节点而不是Y。

4、最后在数组中存在tree【Y】=Y的情况,那么这个点就是老大,也就是存在一棵树,也就是存在一组关系。

5、最后判断两个人是否有关系,还是需要去寻找两个人的根节点是不是相同,而不是两个人的上一个节点是不是相同,不是tree【X】 == tree【Y】,而是root(X)==root(Y)。

 

算法模型:

for(循环总人数)//初始化数组,一开始人与人之间都没有关系

{

      tree【i】=i;

}

update(x,y)//X和Y有关系就更新两个节点的关系

{

         rootx=root(x);

         rooty=root(y);

         if(rootx==rooty)两个点已经有关系不用更新

              return;

         else

              tree【rooty】 = rootx;两个点没有关系,那么就让Y的根节点的值等于X的根节点的值,那么Y下面所有的有关系的人,当他们找到Y的时候就会下一步找到X的根节点了,就和X同一个根节点了,也就是有关系了。

}

 

root(x)//寻找根节点,有两种方法,一种是一直循环查到底,一种是递归同时更新所有相同关系的人为相同的节点,来减少下一次查询的时间

{

        if(tree【x】==x)

             return  x;//已经是根节点了返回。

        else

         tree【x】 = root(tree【x】)//递归更新所有相同关系的人为相同的节点(最终节点)

          return tree【x】;

}

 

#include<cstdio>
#include<cstdlib>
#include<iostream>

using namespace std;
/*并查集*/

int tree[100];

int getRoot2(int t)
{
    int node = t;
    while (tree[node] != node)//不用递归每次都找一遍,时间复杂度较高
    {
        node = tree[node];
    }
    return node;
}

int getRoot(int t)
{
    if (tree[t] == t)
        return t;

    tree[t] = getRoot2(tree[t]);//利用递归不断的更新自己的最终节点,使得有关系的几个人拥有尽可能多的相同的节点
    return tree[t];
}


void update(int x,int y)
{
    int roota;
    int rootb;

    roota = getRoot(x);//获取根节点
    rootb = getRoot(y);

    if(tree[roota] == tree[rootb])//这两个人已经有关系
        return;

    tree[rootb] = roota;//这两个之前没有关系,现在要建立关系了
    return;

}


int main()
{
    int i;//循环变量
    int n,m;
    int x,y;//暂存用户输入有关系的两个人
    int sum=0;//用于保存最终输出的结果
    cin>>n>>m;//总共最多有n人,m组关系

    for (i = 1; i <= n; i++)//初始化,任意两人都没有关系
    {
        tree[i] = i;
    }

    for (i = 1; i <= m; i++)
    {
        cin>>x>>y;//用户输入有关系的两个人
        update(x,y);//更新关系
    }

    for (i = 1; i <= n; i++)//用于输出一共有多少组毫不相干的关系(一共有几颗树)
    {
        if(tree[i] == i)
            sum++;
    }

    cout<<"一共有"<<sum<<"组关系"<<endl;
    for (i = 1; i <= n; i++)
        cout<<tree[i]<<" ";
    cout<<endl;

    while (cin>>x>>y)//输入两个数,看这两个人之间是否有关系
    {
        if(getRoot(x) == getRoot(y))
            cout<<"YES"<<endl;
        else
            cout<<"NO"<<endl;
    }
}

/*

10 9
1 2
3 4
5 2
4 6
2 6
8 7
9 7
1 6
2 4
Result:3
getRoot2:5 1 5 3 5 3 8 9 9 10
getRoot1:5 5 5 5 5 5 8 9 9 10
1 2 3 4 5 6 7 8 9 10

10 7
2 4
5 7
1 3
8 9
1 2
5 6
2 3
Result:4
*/

 

总结一下,这个算法对于关系的处理上面真的很不错,很容易就能理清楚非常复杂的关系,而且递归更新使得算法本身速度快上不少,即使后面数据量大也不怕了。递归更新,在这里也叫路经压缩。

以上是关于并查集的主要内容,如果未能解决你的问题,请参考以下文章

想要学会并查集吗?看我四十行代码实现它

树--12---并查集

笔记并查集---无向图处理代码模板及类型题

并查集

力扣 每日一题 886. 可能的二分法难度:中等,rating: 1794(并查集 / 拆点优化的扩展域并查集)

并查集