LeetCode 721 账户合并 题解

本文解析如何利用并查集算法解决LeetCode中的账户合并问题,通过遍历邮箱地址判断账户归属,合并具有相同邮箱的账户,并保持最终结果按名称排序。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

LeetCode 721 账户合并 题解

给定一个列表 accounts,每个元素 accounts[i] 是一个字符串列表,其中第一个元素 accounts[i][0] 是 名称 (name),其余元素是 emails 表示该账户的邮箱地址。
现在,我们想合并这些账户。如果两个账户都有一些共同的邮箱地址,则两个账户必定属于同一个人。请注意,即使两个账户具有相同的名称,它们也可能属于不同的人,因为人们可能具有相同的名称。一个人最初可以拥有任意数量的账户,但其所有账户都具有相同的名称。
合并账户后,按以下格式返回账户:每个账户的第一个元素是名称,其余元素是按顺序排列的邮箱地址。账户本身可以以任意顺序返回。
示例 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"。 
第二个 JohnMary 是不同的人,因为他们的邮箱地址没有被其他帐户使用。
可以以任何顺序返回这些列表,例如答案 [['Mary''mary@mail.com']['John''johnnybravo@mail.com']['John''john00@mail.com''john_newyork@mail.com''johnsmith@mail.com']] 也是正确的。
 提示:
accounts的长度将在[11000]的范围内。
accounts[i]的长度将在[110]的范围内。
accounts[i][j]的长度将在[130]的范围内。

解题思路:还是并查集

本题的题意比较直观,思路也比较清晰。可以使用【并查集】来解决
根据题意可知:

存在相同邮箱的账号一定属于同一个人【账户名也会相同】
名称相同的账户不一定属于同一个人

思路

由于名称相同无法判断为同1人,所以只能使用邮箱是否相同来判断是否为同一人。
这样建立并查集就比较简单了:

  • 先初始化每个账户为1个连通分量
  • 遍历每个账户下的邮箱,判断该邮箱是否在其他账户下出现
  • 如果未出现,继续
  • 如果账户A、B下出现了相同的邮箱email,那么将账户A和账户B两个连通分量进行合并
  • 最后遍历并查集中每个连通分量,将所有连通分量内部账户的邮箱全部合并(相同的去重,不同的合并)
  • 结束
    针对具体的实现,大家可以看看代码
    举例

在这里插入图片描述

class Solution {
    public List<List<String>> accountsMerge(List<List<String>> accounts) {
        // 作用:存储每个邮箱属于哪个账户 ,同时 在遍历邮箱时,判断邮箱是否出现过[去重]
        // 格式:<邮箱,账户id>
        Map<String, Integer> emailToId = new HashMap<>();
        int n = accounts.size();//id个数
        UnionFind myUnion = new UnionFind(n);
        for(int i = 0; i < n; i++){
            int num = accounts.get(i).size();
            for(int j = 1; j < num; j++){
                String curEmail = accounts.get(i).get(j);
                //当前邮箱没有出现过
                if(!emailToId.containsKey(curEmail)){
                    emailToId.put(curEmail, i);
                }else{//当前邮箱已经出现过,那么代表这两个用户是同一个
                    myUnion.union(i, emailToId.get(curEmail));
                }
            }
        }
        //进行完上面的步骤,同一个用户的所有邮箱已经属于同一个连通域了,但是就算在同一个连通域,不同的邮箱还是可能会对应不同的id
        // 作用: 存储每个账户下的邮箱
        // 格式: <账户id, 邮箱列表> >
        // 注意:这里的key必须是账户id,不能是账户名称,名称可能相同,会造成覆盖
        Map<Integer, List<String>> idToEmails = new HashMap<>();
         //将同一个连通域内的邮箱对应到同一个id【也就是第一次出现的id,比如4、5在同一个连通域,那么这个连通域对应的id就是4】
        for(Map.Entry<String, Integer> entry : emailToId.entrySet()){
            int id = myUnion.find(entry.getValue());
            List<String> emails = idToEmails.getOrDefault(id, new ArrayList<>());
            emails.add(entry.getKey());
            idToEmails.put(id,emails);
        }
        //经过上面的步骤,已经做到了id和邮箱集合对应起来,接下来把用户名对应起来就可以了
        List<List<String>> res = new ArrayList<>();
        for(Map.Entry<Integer, List<String>> entry : idToEmails.entrySet()){
            List<String> emails = entry.getValue();
            Collections.sort(emails);
            List<String> tmp = new ArrayList<>();
            tmp.add(accounts.get(entry.getKey()).get(0));//先添加用户名
            tmp.addAll(emails);
            res.add(tmp);
        }
        return res;
    }
}

class UnionFind {
    int[] parent;

    public UnionFind(int n) {
        parent = new int[n];
        for (int i = 0; i < n; i++) {
            parent[i] = i;
        }
    }

    public void union(int index1, int index2) {
        parent[find(index2)] = find(index1);
    }

    public int find(int index) {
        if (parent[index] != index) {
            parent[index] = find(parent[index]);
        }
        return parent[index];
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值