LeetCode刷题(每日一题) --721. 账户合并(并查集)

题目

给定一个列表 accounts,每个元素 accounts[i] 是一个字符串列表,其中第一个元素 accounts[i][0] 是 名称 (name),其余元素是 emails 表示该账户的邮箱地址。

现在,我们想合并这些账户。如果两个账户都有一些共同的邮箱地址,则两个账户必定属于同一个人。请注意,即使两个账户具有相同的名称,它们也可能属于不同的人,因为人们可能具有相同的名称。一个人最初可以拥有任意数量的账户,但其所有账户都具有相同的名称。

合并账户后,按以下格式返回账户:每个账户的第一个元素是名称,其余元素是按字符 ASCII 顺序排列的邮箱地址。账户本身可以以任意顺序返回。

示例 1:

输入:
accounts = [["John", "johnsmith@mail.com", "john00@mail.com"], ["John", "johnnybravo@mail.com"], ["John", "johnsmith@mail.com", "john_newyork@mail.com"], ["Mary", "mary@mail.com"]]
输出:
[["John", 'john00@mail.com', 'john_newyork@mail.com', 'johnsmith@mail.com'],  ["John", "johnnybravo@mail.com"], ["Mary", "mary@mail.com"]]
解释:
第一个和第三个 John 是同一个人,因为他们有共同的邮箱地址 "johnsmith@mail.com"。 
第二个 John 和 Mary 是不同的人,因为他们的邮箱地址没有被其他帐户使用。
可以以任何顺序返回这些列表,例如答案 [['Mary''mary@mail.com']['John''johnnybravo@mail.com']['John''john00@mail.com''john_newyork@mail.com''johnsmith@mail.com']] 也是正确的。

提示:

accounts的长度将在[1,1000]的范围内。
accounts[i]的长度将在[1,10]的范围内。
accounts[i][j]的长度将在[1,30]的范围内。

思路

还无疑问,今天还是并查集。本来以为可以做的很快,然而我还是太天真了。

解析

  • 按照题目描述,毫无疑问的可以使用并查集来解决。首先每个连通图的基本就是一个用户,而所有的邮箱则是根据不同的用户来进行操作。而出现在不同容器中的用户的邮箱只要相同,则可以判断他们为一个用户。
  • 并查集的框架好说,难点是怎么构造并查集。首先,这里有两个变量:用户,邮箱。按照经验,肯定是用邮箱来操作并查集结构。但是,我们怎么判断两个邮箱可以合并呢?按照常规步骤,先对每一个邮箱创建一个email_to_index的映射(将邮箱映射为index(整型变量))便于UF的操作,同时去除相同的邮箱!!紧接着就是合并的问题。
  • 如果要将邮箱按照拥有者来进行构造图,就必须还要其相关的拥有者。其实这里可以直接按照所给的accounts来进行操作,因为每一组其实就是一个用户及其邮箱的对应,因此,可以直接通过其进行循环,将每一组依次合并。但是,你可能要问了,加入出现在不同组的同一个邮箱,那岂不是又回分派在其他图,从而合并了吗?不会,可以看看上一步,在创建email_to_index的映射的时候,已经将重复的邮箱去掉了,只要有一个邮箱出现在别人的账号之中,那么,这个邮箱就会被忽略。这样,我们就可以进行合并了。可能你又要问,那出与重复邮箱出现在同一个组的其他邮箱怎么办?我们在想一想并查集的作用,当我们先把重复的邮箱放进一个连通图之后,我们只要按照循环来将其伙伴与重复邮箱合并,那么这整个组就会被另一个组合并,也就是同一个拥有者的所有邮箱。(可能语言描述的不太好理解,建议看看代码)
  • 通过以上的步骤,将email按照拥有者构造好了连通图,按照题目要求,我们要返回不同拥有者以及其所有的邮箱集合。可以按照并查集里序号再来构造一个index_to_email,这里的index是置一个连通图中的根节点(通过这个邮箱可以访问到所有邮箱),这样的话,我们就可以按照以下逻辑:
    email_1->email_1_index->uf.Find(email_1_index)->email_root_index->email_root->name
    建立起待查的邮箱以及其拥有者的联系。但是按照以上逻辑,还必须建立起email_to_name的一个容器,不然无法根据email去找到拥有者的姓名。
    **综上:**我们一共需要创建三个容器映射:email_to_name,email_to_index,index_to_email

代码

class Solution {
public:
    struct UF
    {
        vector<int> uf;
        UF(int num)
        {
            uf.resize(num);
            for(int i=0;i<num;++i)
            {
                uf[i] = i;
            }
        }

        int Find(int index)
        {
            if(uf[index]!=index) uf[index]=Find(uf[index]);
            return uf[index];
        }

        void Union(int index1,int index2)
        {
            int found_1 = Find(index1);
            int found_2 = Find(index2);
            if(found_1==found_2) return;
            uf[found_1] = found_2;
            return;
        }
    };
    vector<vector<string>> accountsMerge(vector<vector<string>>& accounts) {
        map<string,int> email_to_index;
        map<string,string> email_to_name;
        int num = accounts.size(),index=0;
        //1.将email分别映射到index和name
        for(int i=0;i<num;++i)
        {
            string name = accounts[i][0];
            for(int j=1;j<accounts[i].size();++j)
            {
                string email = accounts[i][j];
                //查看是否已经包含,没有的话则分配新的id,同时将它指向对应的name。如果有的话,就说明已经出现在前面,是已经创建过的
                if(!email_to_index.count(email))
                {
                    email_to_index[email] = index++;
                    email_to_name[email] = name;
                }
            }
        }
        //2.构造并查集,根据同一个name里面进行合并,一个连通图就是一个姓名的所有email,这里的是所有不同的个数
        UF uf(index);
        for(int i=0;i<num;++i)
        {
            //先获取第一个email以及其的当前对应序号(一个email一个index,不会出现相同的)
            string first_email = accounts[i][1];
            int first_index = email_to_index[first_email];
            //将同一个姓名之内的进行union;
            for(int j=2;j<accounts[i].size();++j)
            {
                string second_email = accounts[i][j];
                int second_index = email_to_index[second_email];
                uf.Union(second_index, first_index);
            }
        }
        //3.通过前两步,此时的连通图就是代表着一个人所拥有的所有email,因此,需要通过这去查找
        map<int,vector<string>> index_to_email;
        //通过Find函数,将拥有相同name的email组合在一起,并且可以通过map的int去查找该name
        for(map<string,int>::iterator it=email_to_index.begin();it!=email_to_index.end();++it)
        {
            index_to_email[uf.Find(it->second)].push_back(it->first);
        }
        vector<vector<string>> result;
        for(map<int,vector<string>>::iterator it = index_to_email.begin();it!=index_to_email.end();++it)
        {
            sort(it->second.begin(),it->second.end());
            string name = email_to_name[it->second[0]];
            it->second.insert(it->second.begin(),name);
            result.push_back(it->second);
        }
        return result;
    }
};
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Hanzoe_lwh

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值