Given a list accounts, 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 email that is common 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", "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:
The first and third John's are the same person as they have the common email "johnsmith@mail.com".
The second 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.

Note:

  • The length of accounts will be in the range [1, 1000].
  • The length of accounts[i] will be in the range [1, 10].
  • The length of accounts[i][j] will be in the range [1, 30].

这道题给了一堆人名和邮箱,一个名字可能有多个邮箱,但是一个邮箱只属于一个人,让我们把同一个人的邮箱都合并到一起,名字相同不一定是同一个人,只有当两个名字有相同的邮箱,才能确定是同一个人,题目中的例子很好说明了这个问题,输入有三个 John,最后合并之后就只有两个了。这道题博主最开始尝试使用贪婪算法来做,结果发现对于下面这个例子不适用:

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

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

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

可以看到其实这三个 John 是同一个人,但是贪婪算法遍历完前两个 John,还是认为其是两个不同的人,当遍历第三个 John 时,就直接加到第一个 John 中了,而没有同时把第二个 John 加进来,也可能博主写的是假的贪婪算法,反正不管了,还是参考大神们的解法吧。这个归组类的问题,最典型的就是岛屿问题(例如 Number of Islands II),很适合使用 Union Find 来做,LeetCode 中有很多道可以使用这个方法来做的题,比如 Friend CirclesGraph Valid TreeNumber of Connected Components in an Undirected Graph,和 Redundant Connection 等等。都是要用一个 root 数组,每个点开始初始化为不同的值,如果两个点属于相同的组,就将其中一个点的 root 值赋值为另一个点的位置,这样只要是相同组里的两点,通过 find 函数得到相同的值。在这里,由于邮件是字符串不是数字,所以 root 可以用 HashMap 来代替,还需要一个 HashMap 映射owner,建立每个邮箱和其所有者姓名之前的映射,另外用一个 HashMap 来建立用户和其所有的邮箱之间的映射,也就是合并后的结果。

首先遍历每个账户和其中的所有邮箱,先将每个邮箱的 root 映射为其自身,然后将 owner 赋值为用户名。然后开始另一个循环,遍历每一个账号,首先对帐号的第一个邮箱调用 find 函数,得到其父串p,然后遍历之后的邮箱,对每个遍历到的邮箱先调用 find 函数,将其父串的 root 值赋值为p,这样做相当于将相同账号内的所有邮箱都链接起来了。接下来要做的就是再次遍历每个账户内的所有邮箱,先对该邮箱调用 find 函数,找到父串,然后将该邮箱加入该父串映射的集合汇总,这样就就完成了合并。最后只需要将集合转为字符串数组,加入结果 res 中,通过 owner 映射找到父串的用户名,加入字符串数组的首位置,参见代码如下:

解法一:

class Solution {
public:
vector<vector<string>> accountsMerge(vector<vector<string>>& accounts) {
vector<vector<string>> res;
unordered_map<string, string> root;
unordered_map<string, string> owner;
unordered_map<string, set<string>> m;
for (auto account : accounts) {
for (int i = ; i < account.size(); ++i) {
root[account[i]] = account[i];
owner[account[i]] = account[];
}
}
for (auto account : accounts) {
string p = find(account[], root);
for (int i = ; i < account.size(); ++i) {
root[find(account[i], root)] = p;
}
}
for (auto account : accounts) {
for (int i = ; i < account.size(); ++i) {
m[find(account[i], root)].insert(account[i]);
}
}
for (auto a : m) {
vector<string> v(a.second.begin(), a.second.end());
v.insert(v.begin(), owner[a.first]);
res.push_back(v);
}
return res;
}
string find(string s, unordered_map<string, string>& root) {
return root[s] == s ? s : find(root[s], root);
}
};

下面这种方法是使用 BFS 来解的,建立了每个邮箱和其所有出现的账户数组之间的映射,比如还是这个例子:

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

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

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

那么建立的映射就是:

"a@gmail.com" -> [0, 2]

"b@gmail.com" -> [0]

"c@gmail.com" -> [1, 2]

"d@gmail.com" -> [1]

然后还需要一个 visited 数组,来标记某个账户是否已经被遍历过,0表示为未访问,1表示已访问。在建立好 HashMap 之后,遍历所有的账户,如果账户未被访问过,将其加入队列 queue,新建一个 TreeSet,此时进行队列不为空的 while 循环,取出队首账户,将该该账户标记已访问1,此时将该账户的所有邮箱取出来放入数组 mails 中,然后遍历 mails 中的每一个邮箱,将遍历到的邮箱加入 TreeSet 中,根据映射来找到该邮箱所属的所有账户,如果该账户未访问,则加入队列中并标记已访问。当 while 循环结束后,当前账户的所有合并后的邮箱都保存在 TreeSet 中,将其转为字符串数组,并且加上用户名在首位置,最后加入结果 res 中即可,参见代码如下:

解法二:

class Solution {
public:
vector<vector<string>> accountsMerge(vector<vector<string>>& accounts) {
vector<vector<string>> res;
int n = accounts.size();
unordered_map<string, vector<int>> m;
vector<int> visited(n, );
for (int i = ; i < n; ++i) {
for (int j = ; j < accounts[i].size(); ++j) {
m[accounts[i][j]].push_back(i);
}
}
for (int i = ; i < n; ++i) {
if (visited[i] != ) continue;
queue<int> q{{i}};
set<string> s;
while (!q.empty()) {
int t = q.front(); q.pop();
visited[t] = ;
vector<string> mails(accounts[t].begin() + , accounts[t].end());
for (string mail : mails) {
s.insert(mail);
for (int user : m[mail]) {
if (visited[user] != ) continue;
q.push(user);
visited[user] = ;
}
}
}
vector<string> out(s.begin(), s.end());
out.insert(out.begin(), accounts[i][]);
res.push_back(out);
}
return res;
}
}; 
 
Github 同步地址:
 

类似题目:

Redundant Connection

Sentence Similarity II

Sentence Similarity

参考资料:

https://leetcode.com/problems/accounts-merge/

https://leetcode.com/problems/accounts-merge/discuss/109189/simple-c-sol

https://leetcode.com/problems/accounts-merge/discuss/109157/javac-union-find

https://leetcode.com/problems/accounts-merge/discuss/109158/java-solution-build-graph-dfs-search

[LeetCode] Accounts Merge 账户合并的更多相关文章

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

    Given a list of accounts where each element accounts[i] is a list of strings, where the first elemen ...

  2. [LeetCode] 721. Accounts Merge 账户合并

    Given a list accounts, each element accounts[i] is a list of strings, where the first element accoun ...

  3. [leetcode]721. Accounts Merge账户合并

    Given a list accounts, each element accounts[i] is a list of strings, where the first element accoun ...

  4. [LeetCode] 56 - Merge Intervals 合并区间

    Given a collection of intervals, merge all overlapping intervals. For example,Given [1,3],[2,6],[8,1 ...

  5. LeetCode 56. Merge Intervals 合并区间 (C++/Java)

    题目: Given a collection of intervals, merge all overlapping intervals. Example 1: Input: [[1,3],[2,6] ...

  6. 【LeetCode】721. Accounts Merge 解题报告(Python)

    作者: 负雪明烛 id: fuxuemingzhu 个人博客: http://fuxuemingzhu.cn/ 题目地址: https://leetcode.com/problems/accounts ...

  7. [LeetCode] 21. Merge Two Sorted Lists 合并有序链表

    Merge two sorted linked lists and return it as a new list. The new list should be made by splicing t ...

  8. [LeetCode] 88. Merge Sorted Array 合并有序数组

    Given two sorted integer arrays nums1 and nums2, merge nums2 into nums1 as one sorted array. Note: T ...

  9. Java实现 LeetCode 721 账户合并(并查集)

    721. 账户合并 给定一个列表 accounts,每个元素 accounts[i] 是一个字符串列表,其中第一个元素 accounts[i][0] 是 名称 (name),其余元素是 emails ...

随机推荐

  1. C语言描述二叉树的实现及操作(链表实现)

    概述     二叉树为每个节点最多有两个儿子节点(左儿子节点和右儿子节点)的树. 前序遍历:根结点 ---> 左子树 ---> 右子树. 中序遍历:左子树---> 根结点 ---&g ...

  2. Konckout第二个实例:数组数据类型双向绑定 -- 下拉select

    自定义js做法: <!DOCTYPE html> <html> <head> <meta charset="utf-8" /> &l ...

  3. Java多线程:Callable,Future,FutureTask

    一.Future Future和Callable基本是成对出现的,Callable负责产生结果,Future负责获取结果.     1.Callable接口类似于Runnable,只是Runnable ...

  4. kill-mysql-sleep.sh

    #!/bin/bash #while : #do n=`/usr/bin/mysqladmin -uroot -pXXXXX processlist | grep -i sleep | wc -l` ...

  5. 理解JAVA内存模型

    实际上java内存模型是如上图所示一样 每个线程有自己的栈内存,存放共享对象的副本,本地变量 每个线程自己的本地变量是不可见的,但是共享对象对每个线程都是可见的. 如果想实现线程通信的话, 线程对共享 ...

  6. python 面向对象的程序设计

    一:什么是编程范式? 编程是程序员用特定的语法 + 数据结构 + 算法组成的代码来告诉计算机如何执行任务的过程. 如果把编程的过程比喻为练习武功,那么编程范式指的就是武林中的各种流派,而在编程的世界里 ...

  7. 201621123050 《Java程序设计》第7周学习总结

    1. 本周学习总结 1.1 思维导图:Java图形界面总结 2.书面作业 1. GUI中的事件处理 1.1 写出事件处理模型中最重要的几个关键词. 1.事件:用户的操作,例如点击或输入之类的操作 2. ...

  8. WingIDE5.*注册破解方法

    WingIDE是Python程序语言设计的集成开发环境,具有语法标签高亮显示,命令自动完成和函数跳转列表等非常强大的功能.本文主要介绍WingIDE 5安装及注册破解方法. 1. WingIDE 5下 ...

  9. nyoj 回文字符串

    回文字符串 时间限制:3000 ms  |  内存限制:65535 KB 难度:4   描述 所谓回文字符串,就是一个字符串,从左到右读和从右到左读是完全一样的,比如"aba".当 ...

  10. python之路--day6--字符编码

    一.知识储备 cpu--控制和运算 内存--暂时存储cpu需要的数据 硬盘--永久保存数据2.文本编辑器的原理存储原理 1,启动文本编辑器 2,在编辑器上输入内容---此时输入内容还在内存上 3,保存 ...