今日头条的并查集题

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/xiongchengluo1129/article/details/82285733
入秋以来,困意绵绵,特此刷题,清醒一下。

题目如下:
Bytedance Efficiency Engineering 团队在8月20日搬入了学清嘉大厦,为庆祝团队的乔迁之喜,字节军决定邀请EE团队,举办一个大型交友会。每每题目说道这里肯定有一个问题,那么问题如下:
EE团队一共有N个人,大家都比较害羞,不善于和陌生人交流,这N个人都向字节君提供了自己认识人的名字,但不包括自己(我想是因为一个人可能一生都没有办法清醒的认识到自己吧)。如果A的名单里有B,或者B的吗名单里有A,则代表A和B是互相认识的。同时,如果A认识B,B认识C,则代表A,C也会很快认识,毕竟是通过B这个小媒婆介绍的,两人就可以很快互相认识了。
字节君打算将整个团队分成M组,每组内人员全都可以通过间接或者直接的方式互相认识,而组间的人员互相都不认识。
请您确定一个方案,确定m的最小值。

输入描述:
第一行一个整数n,代表这个团队一共有n个人,从1开始编号。
接下来有n行,第x+1行代表编号为x的人认识的人的编号 k (1<=k<=n),每个人的名单以0代表结束。

输出描述:
一个整数m,代表可以分割的最小的组的个数。

输入实例:
10
0
5 3 0
8 4 0
9 0
9 0
3 0
0
7 9 0
0
9 7 0
输出:
2

  一开始看这题,以为是要求图的联通分量,又要开辟空间,效率很定不高。所以改为用并查集来做这件事情,因为并查集可以很好的解决这种相亲交友,道路修建问题。

  贴一下百度百科关于并查集的定义
  并查集,在一些有N个元素的集合应用问题中,我们通常是在开始时让每个元素构成一个单元素的集合,然后按一定顺序将属于同一组的元素所在的集合 合并,其间要反复查找一个元素在哪个集合中。并查集是一种树型的数据结构,用于处理一些不相交集合(Disjoint Sets)的合并及查询问题。常常在使用中以森林来表示。
  下面说一说并查集的两个最重要的操作,

一、查找操作:

//将此团队中的每个人进行编号,id[x]存储了标号为x的人它的祖先,例如,id[3]=6,则说明,编号为3的这个人,它的祖先的编号为6,类似于树的存储结构。
//如果,这个人他很孤单,它不认识任何人,那么他的祖先就是他自己,即id[3]=3
int Find(int x) // x为某个人的编号
{
    while (x!=id[x])  //他不是一个人,所以他是一定有祖先的
    {
        id[x]=id[id[x]];//路径压缩   类似于两边之和大于第三边,用第三条边来代替另外两条边,起到路径压缩的作用,同时又不影响找祖先,这是我觉得最简便的写法了,
        x=id[x];
    }
    return x;  //最终转换到根节点
}

二、合并操作:

void Union(int p,int q) //找 p,q两个人的祖先,看谁包含的元素多,就将另一个根节点挂到这个元素多的根节点上。也就是弱者被吞并了。
{
//sz[p]存储的是以p为根节点的子树的节点数。
    int fp,fq;
    fp=Find(p);
    fq=Find(q);
    if (sz[fp]>=sz[fq])//节点数小的树连接到节点数大的树上
    {
        id[fq]=fp;
        sz[fp]+=sz[fq];
    }
    else
    {
        id[fp]=fq;
        sz[fq]+=sz[fp];
    }
}

也可以加一个判断连通性的操作:

bool connected(int p,int q)
{
    if (Find(p)==Find(q))
    {
        return true;
    }
    return false;
}

本题的全部代码:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int N(100001);
int n,m,Q;
int sz[N];//以当前节点为根的子树中节点的个数,即为权重
int id[N];
bool ff[N]= {0};
int Find(int x)
{
    while (x!=id[x])
    {
        id[x]=id[id[x]];//路径压缩
        x=id[x];
    }
    return x;
}
void Union(int p,int q)
{
    int fp,fq;
    fp=Find(p);
    fq=Find(q);
    if (sz[fp]>=sz[fq])//节点数小的树连接到节点数大的树上
    {
        id[fq]=fp;
        sz[fp]+=sz[fq];
    }
    else
    {
        id[fp]=fq;
        sz[fq]+=sz[fp];
    }
}
bool connected(int p,int q)
{
    if (Find(p)==Find(q))
    {
        return true;
    }
    return false;
}
int main()
{
    scanf("%d",&n);
    for (int i=1; i<=n; i++) //节点的标号从1开始的不是从0开始
    {
        id[i]=i;
        sz[i]=1;
    }
    for (int i=1; i<=n; i++)
    {
        int t;
        while (scanf("%d",&t))
        {
            if (t!=0)
            {

                Union(i,t);
            }
            else
            {
                break;
            }
        }

    }
    int ans=0;
    for (int i=1; i<=n; i++)
    {
        if(i==id[i])  //i是一个树的根节点
            ans++;
    }
    printf("%d\n",ans);
    return 0;
}
还有一点输入输出的技巧没记完,下午接着写写,好饿好饿,去吃饭了~~~~~~

猜你喜欢

转载自blog.csdn.net/xiongchengluo1129/article/details/82285733
今日推荐