力扣:账户合并

题目描述


  给定一个列表 accounts,每个元素 accounts[i] 是一个字符串列表,其中第一个元素 accounts[i][0] 是 名称 (name),其余元素是 emails 表示该帐户的邮箱地址。
  现在,我们想合并这些帐户。如果两个帐户都有一些共同的邮件地址,则两个帐户必定属于同一个人。请注意,即使两个帐户具有相同的名称,它们也可能属于不同的人,因为人们可能具有相同的名称。一个人最初可以拥有任意数量的帐户,但其所有帐户都具有相同的名称。
  合并帐户后,按以下格式返回帐户:每个帐户的第一个元素是名称,其余元素是按顺序排列的邮箱地址。accounts 本身可以以任意顺序返回。
力扣:721 账户合并

Input:
accounts = [
  [“John”, “johnsmith@mail.com”, “john00@mail.com”],
  [“John”, “johnnybravo@mail.com”],
  [“John”, “johnsmith@mail.com”, “john_newyork@mail.com”],
  [“Mary”, “mary@mail.com”]
]
Output: [
 [“John”, ‘john00@mail.com’, ‘john_newyork@mail.com’, ‘johnsmith@mail.com’],
 [“John”, “johnnybravo@mail.com”],
 [“Mary”, “mary@mail.com”]
]


Explanation:
第一个和第三个 John 是同一个人,因为他们有共同的电子邮件 “johnsmith@mail.com”。
第二个 John 和 Mary 是不同的人,因为他们的电子邮件地址没有被其他帐户使用。

题目分析


题目要求很明确,通过具有相同的邮件,确认两个相同姓名的用户是否是同一个用户,是则返回该用户的所有邮箱的合集。

1、构建Map ,保存邮箱到姓名的关系,但因为姓名是存在重复的,所以不能以姓名作为并查集的根,这里想到了采用 accounts 的下标。

Map<String , Integer> parent = new HashMap<>();

2、此外,还需要构建一个中间结果Map , 保存下标和邮箱的映射,此时的邮箱是去重后的(该下标代表的用户,所拥有的的所有邮箱),采用 TreeSet 进行去重处理。

Map<Integer, TreeSet<String>> temp = new HashMap<>();

3、从中间结果 temp 中还原出 List<List> ,通过下标获取 accounts 的姓名,并将姓名和邮箱添加到同一个 List 得到一个用户和所有的邮箱。

int root = entry.getKey();
String name = accounts.get(root).get(0);
list.add(name);
list.addAll(entry.getValue());

AC代码:

class Solution {
	
	// 查找根
    public int find(int[] father , int x){
        return father[x] == -1 ? x : find(father , father[x]);
    }
	
	// 合并并查集
    public void union(int[] father , int x , int y){
        int x_parent = find(father , x);
        int y_parent = find(father , y);
        if(x_parent != y_parent){
            father[y_parent] = x_parent;
        }
    }

    public List<List<String>> accountsMerge(List<List<String>> accounts) {
        Map<String , Integer> parent = new HashMap<>();
        int[] father = new int[accounts.size()];
        Arrays.fill(father , -1);
        // 构建 姓名和邮箱 之间的并查集
        for(int i = 0 ; i < accounts.size(); i ++){
            List<String> account = accounts.get(i);
            for(int j = 1 ; j < account.size() ; j ++){
                String email = account.get(j);
                // 如果邮箱已经存在,说明存在集合是属于同一个用户的,将其合并
                if(parent.containsKey(email)){
                    union(father , parent.get(email) , i);
                }
                // 如果不存在,加入
                parent.put(email , i);
            }
        }
        
        // 将并查集根相同,邮箱合并到一起,得到中间结果 temp 
        Map<Integer , Set<String>> temp = new HashMap<>();
        for(int i = 0 ; i < accounts.size() ; i ++){
            int root = find(father , i);
            List<String> account = accounts.get(i);
            List<String> emails = account.subList(1 , account.size());
            // 如果 根 已经出现过,现在只需要将邮箱合并,因为是 Set 无需考虑是否会重复
            if(temp.containsKey(root)){
                temp.get(root).addAll(emails);
            } else {
            // 如果没有出现 此根 ,构建一个 Set 集合,将邮箱加入
                temp.put(root , new TreeSet<>(emails));
            }
        }

        // 将中间结果 temp 转换为输出的返回类型
        List<List<String>> result = new ArrayList<>();
        for(Map.Entry<Integer , Set<String>> entry : temp.entrySet()){
            List<String> list = new ArrayList<>();
            int root = entry.getKey();
            String name = accounts.get(root).get(0); // 通过下标获取姓名
            list.add(name); // 将姓名添加到 list
            list.addAll(entry.getValue()); // 然后将邮箱添加到 list 
            result.add(list);
        }
        return result;
    }
}
上一篇:Java中的super和this


下一篇:JavaScript 圣杯模式笔记