721、合并账户

  1. 题目
    1. 示例 1:
    2. 示例 2:
  2. 分析
    1. 哈希表处理数据,并查集
    2. 哈希表处理数据,并查集完成数据映射合并

题目

给定一个列表 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']] 也是正确的。

示例 2:

输入:accounts = [["Gabe","Gabe0@m.co","Gabe3@m.co","Gabe1@m.co"],["Kevin","Kevin3@m.co","Kevin5@m.co","Kevin0@m.co"],["Ethan","Ethan5@m.co","Ethan4@m.co","Ethan0@m.co"],["Hanzo","Hanzo3@m.co","Hanzo1@m.co","Hanzo0@m.co"],["Fern","Fern5@m.co","Fern1@m.co","Fern0@m.co"]]
输出:[["Ethan","Ethan0@m.co","Ethan4@m.co","Ethan5@m.co"],["Gabe","Gabe0@m.co","Gabe1@m.co","Gabe3@m.co"],["Hanzo","Hanzo0@m.co","Hanzo1@m.co","Hanzo3@m.co"],["Kevin","Kevin0@m.co","Kevin3@m.co","Kevin5@m.co"],["Fern","Fern0@m.co","Fern1@m.co","Fern5@m.co"]]

提示:

  • 1 <= accounts.length <= 1000
  • 2 <= accounts[i].length <= 10
  • 1 <= accounts[i][j].length <= 30
  • accounts[i][0] 由英文字母组成
  • accounts[i][j] (for j > 0) 是有效的邮箱地址

分析

哈希表,key需要处理一下作区分。

遍历列表,加入到hash表中,进行合并。

一个key,遍历其邮件列表,发现有重复进行账户合并。
否则,创建新的key,加入到map中。

处理排序问题!!!

最后,将map转成List。

哈希表处理数据,并查集

进行三次查找,判断有交集-合并操作!!! 耗时过长!!!

public List<List<String>> accountsMerge(List<List<String>> accounts) {
        // 哈希表
        Map<String, Set<String>> accountMap = new TreeMap<>();
        // 遍历每一个账户
        for (List<String> accountList : accounts) {
            boolean needAddNew = true;
            int index = 0;
            // 相同名字可以有1000个,遍历历史的哈希表数据
            for (int k = 0; k <= 1000; k++) {
                // 已经存在了多个name
                String key = accountList.get(0) + "#" + k;
                // 第k个相同名字存在
                if (accountMap.containsKey(key)) {
                    boolean needMerge = false;
                    for (int i = 1; i < accountList.size(); i++) {
                        if (accountMap.get(key).contains(accountList.get(i))) {
                            needMerge = true;
                            break;
                        }
                    }
                    if (needMerge) {
                        needAddNew = false;
//                         System.out.println("merge " + key + " and " + accountList.get(0));
                        for (int i = 1; i < accountList.size(); i++) {
                            accountMap.get(key).add(accountList.get(i));
                        }
                        break;
                    }
                } else {
                    index = k;
                    break;
                }
            }

            if (needAddNew) {
                String key = accountList.get(0) + "#" + index;
//                 System.out.println("add new " + key);
                // 自定义比较器
                Set<String> emails = new TreeSet<>();
                for (int i = 1; i < accountList.size(); i++) {
                    emails.add(accountList.get(i));
                }
                accountMap.put(key, emails);
            }
        }

        // 第一次检查map里面相同name的是否需要merge
        List<String> disableKeys = new ArrayList<>();
        for (Map.Entry<String, Set<String>> entry : accountMap.entrySet()) {
            if (disableKeys.contains(entry.getKey())) {// 已经merge过了
                continue;
            }
            String name = entry.getKey().split("#")[0];
            for (Map.Entry<String, Set<String>> entry1 : accountMap.entrySet()) {
                // 前缀一致 且 不相等 则可以 merge
                if (entry1.getKey().startsWith(name + "#") && !entry1.getKey().equals(entry.getKey())) {
                    if (!Collections.disjoint(entry.getValue(), entry1.getValue())) {// 存在交集,则merge
//                        System.out.println("merge " + entry.getKey() + " and " + entry1.getKey());
                        entry.getValue().addAll(entry1.getValue());
                        disableKeys.add(entry1.getKey());// entry1废弃
                    }
                }
            }
        }

        // 第一次检查map里面相同name的是否需要merge
        for (Map.Entry<String, Set<String>> entry : accountMap.entrySet()) {
            if (disableKeys.contains(entry.getKey())) {// 已经merge过了
                continue;
            }
            String name = entry.getKey().split("#")[0];
            for (Map.Entry<String, Set<String>> entry1 : accountMap.entrySet()) {
                // 前缀一致 且 不相等 则可以 merge
                if (entry1.getKey().startsWith(name + "#") && !entry1.getKey().equals(entry.getKey())) {
                    if (!Collections.disjoint(entry.getValue(), entry1.getValue())) {// 存在交集,则merge
//                        System.out.println("merge " + entry.getKey() + " and " + entry1.getKey());
                        entry.getValue().addAll(entry1.getValue());
                        disableKeys.add(entry1.getKey());// entry1废弃
                    }
                }
            }
        }

        List<List<String>> ans = new ArrayList<>();
        for (Map.Entry<String, Set<String>> entry : accountMap.entrySet()) {
            if (disableKeys.contains(entry.getKey())) {
                continue;
            }
            List<String> list = new ArrayList<>();
            String name = entry.getKey().split("#")[0];
            list.add(name);
            list.addAll(entry.getValue());
            ans.add(list);
        }
        return ans;
    }
}

哈希表处理数据,并查集完成数据映射合并

class Solution {
    public List<List<String>> accountsMerge(List<List<String>> accounts) {
        Map<String, Integer> emailToIndex = new HashMap<String, Integer>();
        Map<String, String> emailToName = new HashMap<String, String>();
        int emailsCount = 0;// 统计全部的email的数量
        for (List<String> account : accounts) {
            String name = account.get(0);
            int size = account.size();
            for (int i = 1; i < size; i++) {
                String email = account.get(i);
                if (!emailToIndex.containsKey(email)) {
                    emailToIndex.put(email, emailsCount++);// 过滤重复的email
                    emailToName.put(email, name);// 将email与Name映射, 不用list的email映射到同一个name
                }
            }
        }
        UnionFind uf = new UnionFind(emailsCount);
        for (List<String> account : accounts) {
            String firstEmail = account.get(1);
            int firstIndex = emailToIndex.get(firstEmail);
            int size = account.size();
            for (int i = 2; i < size; i++) {
                String nextEmail = account.get(i);
                int nextIndex = emailToIndex.get(nextEmail);
                // 将两个email合并,因为他们是同一个name list下的邮箱
                uf.union(firstIndex, nextIndex);
            }
        }

        // 将 email -> index 映射 转换成 index -> email list映射。相同祖先的归为一组
        Map<Integer, List<String>> indexToEmails = new HashMap<>();
        for (String email : emailToIndex.keySet()) {
            int index = uf.find(emailToIndex.get(email));// 并查集查找祖先
            List<String> account = indexToEmails.getOrDefault(index, new ArrayList<>());
            account.add(email);
            indexToEmails.put(index, account);
        }

        // 构造答案数据
        List<List<String>> merged = new ArrayList<>();
        for (List<String> emails : indexToEmails.values()) {
            // 对于每个list排序
            Collections.sort(emails);
            String name = emailToName.get(emails.get(0));// 获取分组的name
            List<String> account = new ArrayList<>();
            account.add(name);
            account.addAll(emails);
            merged.add(account);
        }

        return merged;

    }
}

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];
    }
}

转载请注明来源,欢迎对文章中的引用来源进行考证,欢迎指出任何有错误或不够清晰的表达。可以在下面评论区评论,也可以邮件至 1056615746@qq.com

💰

Title:721、合并账户

Count:1.8k

Author:攀登

Created At:2024-07-15, 11:53:06

Updated At:2024-07-15, 11:58:57

Url:http://jiafeimao-gjf.github.io/2024/07/15/721-%E5%90%88%E5%B9%B6%E8%B4%A6%E6%88%B7/

Copyright: 'Attribution-non-commercial-shared in the same way 4.0' Reprint please keep the original link and author.

×

Help us with donation