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']] 也是正确的。

题解

  1. 相同邮箱的账户一定属于同一个人,相同名称的账户不一定属于同一个人。故使用邮箱来判断是否为同一个人
  2. 初始化每一个账户为1的连通分量
  3. 遍历每个账户下的邮箱,判断该邮箱是否在其他账户下出现过
    1. 若出现过,则表示此邮箱对应的两个账户属于同一个人(例如邮箱A,邮箱B),开始合并邮箱A,邮箱B的连通分量
    2. 若未出现过,继续
  4. 遍历所有的连通分量,将所有连通分量内的账户的所有邮箱合并
class Solution {
    public List<List<String>> accountsMerge(List<List<String>> accounts) {
        Map<String, Integer> emailToId = new HashMap<>();
        int n = accounts.size();
        Unionfind union = new Unionfind(n);
        // 对每个账户下的邮箱进行遍历
        for(int i = 0; i < n; i++) {
            int size = accounts.get(i).size();
            for(int j = 1; j < size; j++) {
                String curEmail = accounts.get(i).get(j);
                // 未出现过则添加
                if(!emailToId.containsKey(curEmail)) {
                    emailToId.put(curEmail, i);
                } else {
                    // 合并连通分量
                    union.unionfind(i, emailToId.get(curEmail))
                }
            }
        }
        // -----------此时每组连通分量就是一个人拥有的全部账号----------------
        // 由于emailToId表示的是邮箱与id之间的关系,所以即使在同一个连通域内,其对应的id也可能不相同,所以要合并将同一连通域内的所有邮箱合并到一个id上
        Map<Integer, List<String>> idToEmails = new HashMap<>();
        for(Map.Entry<String, Integer> entry : emailToId.entrySet()) {
            int id = union.find(entry.getValue());
            List<String> emails = idToEmails.getOrDefault(id, new ArrayList<>());
            emails.add(entry.getKey());
            idToEmails.put(id, emails);
        }
        // ----------此时每个id对应的就是一个人所拥有的全部账户--------------
        // 这是只需要将id与name相对应即可
        List<List<String>> res = new ArrayList<>();
        for(Map.Entry<Integer, List<String>> entry : idToEmails.entrySet()) {
            List<String> emails = entry.getValue();
            Collection.sort(emails);
            List<String> temp = new ArrayList<>();
            temp.add(accounts.get(entry.getKey()).get(0));
            temp.addAll(emails);
            res.add(temp);
        }
        return res;

    }

    class Unionfind{
        int[] parents;

        // 初始化
        public Unionfind(int n) {
            for(int i = 0; i < n; i++) {
                parents[i] = i;
            }
        }

        // 合并连通分量
        public unionfind(int index1, int index2) {
            parents[find(index2)] = find(index1);
        }

        // 找祖先节点
        public int find(int index) {
            if(index != parents[index]) {
                return find(parents[index]);
            }
            return index;
        }
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值