Loading

【leetcode】721. Accounts Merge(账户合并)

  Given a list of accounts where each element accounts[i] is a list of strings, where the first element accounts[i][0] is a name, and the rest of the elements are emails representing emails of the account.

Now, we would like to merge these accounts. Two accounts definitely belong to the same person if there is some common email to both accounts. Note that even if two accounts have the same name, they may belong to different people as people could have the same name. A person can have any number of accounts initially, but all of their accounts definitely have the same name.

After merging the accounts, return the accounts in the following format: the first element of each account is the name, and the rest of the elements are emails in sorted order. The accounts themselves can be returned in any order.

 

Example 1:

Input: accounts = [["John","johnsmith@mail.com","john_newyork@mail.com"],["John","johnsmith@mail.com","john00@mail.com"],["Mary","mary@mail.com"],["John","johnnybravo@mail.com"]]
Output: [["John","john00@mail.com","john_newyork@mail.com","johnsmith@mail.com"],["Mary","mary@mail.com"],["John","johnnybravo@mail.com"]]
Explanation:
The first and second John's are the same person as they have the common email "johnsmith@mail.com".
The third John and Mary are different people as none of their email addresses are used by other accounts.
We could return these lists in any order, for example the answer [['Mary', 'mary@mail.com'], ['John', 'johnnybravo@mail.com'], 
['John', 'john00@mail.com', 'john_newyork@mail.com', 'johnsmith@mail.com']] would still be accepted.

这道题的需求是账户合并,accounts 中每个数组中第一个元素是用户名,后面是其邮箱,最终需要合并求出一个用户下所有的邮箱,这里要注意以下,相同的用户名所属的邮箱不一定合并,除非其用户名下有相同的邮箱。

有一种初略的合并方法,先用set存储一个用户的所有邮箱,然后遍历另外一个同名用户中所有的邮箱,如果在之前用户存储邮箱数组中find到,那么就insert到前一个set中,但是这样会存在一个问题。即

  结果发现对于下面这个例子不适用:

  ["John", "a@gmail.com", "b@gmail.com"]

  ["John", "c@gmail.com", "d@gmail.com"]

  ["John", "a@gmail.com", "c@gmail.com"]

  如果按照遍历顺序不同,第二组的邮箱并不会加入到“John”中,也就是上述融合思路无法传递依赖,对于这种分组问题,还是优先用并查集来做,先分好组,每个邮箱指向一个根邮箱,然后根据根邮箱来找用户,存储下来。所以按照这种思路,那么我们需要构建如下几个数据结构,root 用于映射根邮箱,owner用于邮箱与用户的映射,还需要一个map 来去重的存储根邮箱以及同一组邮箱。根据上述分析代码如下:

       

class Solution {
public:
    vector<vector<string>> accountsMerge(vector<vector<string>>& accounts) {
        // 用什么数据结构最方便的存储这些数据
        // 用map 记录用户与 邮箱的映射 邮箱用set 存储去重?
        // 重点是用什么样的数据结构对上述accounts进行存储 分组
        // 涉及到分组问题 可以考虑用union find进行分组
        vector<vector<string>> res;
        unordered_map<string,string> root;// 每个邮箱指向的根
        unordered_map<string,string> owner;// 每个邮箱指向的用户
        unordered_map<string,set<string>>mm;// 一个根邮箱对应的一组相关联的邮箱
        // 初始化并查集的root
        for(auto account:accounts){
            for(int i=1;i<account.size();++i){
                // 第一个元素是用户名
                root[account[i]]=account[i];// 每个邮箱开始都指向自己
                owner[account[i]]=account[0];
            }
        }
        // 同组邮箱分类
        for(auto account:accounts){
            string p=find(account[1],root);
            for(int i=2;i<account.size();++i){
                root[find(account[i],root)]=p;//union find 更新根节点
            }
        }
        // 同一组邮箱汇总
        for(auto account:accounts){
            for(int i=1;i<account.size();++i){
                mm[find(account[i],root)].insert(account[i]);
            }
        }
        //将同组邮箱 根据根节点映射寻找owner
        for(auto m:mm){
            vector<string> vv(m.second.begin(),m.second.end());
            vv.insert(vv.begin(),owner[m.first]);//在数组开头插入用户名
            res.push_back(vv);
        }
        return res;        
    }
    string find(string s,unordered_map<string,string> &root){
        return s==root[s]?s:(root[s]=find(root[s],root));// 压缩路径并返回每个邮箱映射的根节点
    }
};

 

 





posted @ 2021-11-29 10:38  aalanwyr  阅读(71)  评论(0编辑  收藏  举报