本文默认大家对并查集有一定了解,所以不会去解释并查集的思想和模板。
1、如何找到属于同一个人的账户?
很明显是通过是否有相同的邮箱来判断账户是否属于同一个人。
但这种判断需要我们知道哪些邮箱前面出现过,那么我们如何记录出现过的邮箱呢?
利用哈希表记录:
unordered_map<string, int> um; //表示每个邮箱的所属账户(用下标表示)是谁
我们可以通过um.count(str)判断该邮箱是否出现过
若未出现,则将该邮箱存入um中,达到了记录出现过的邮箱的目的。
那么出现过(即找到属于同一人的账户了之后)该怎么办呢?
这个时候我们就要想办法建立属于同一人的账户之间的联系:
利用并查集建立联系:
此时我要利用并查集将它们合并(并查集意义上的合并)到一起:
merge(um[accounts[i][j]], i);
当所有账户之间的关系都建立起来后,我们可以将账户进行真正意义上的合并
我们将遍历每个账户的每个邮箱,并且将做以下操作:
1、利用并查集建立的联系得到该账户所在集合对应的根账户的下标;
2、将该账户的所有邮箱插入到哈希表umt中(key值为根账户下标,这样保证每个人只有一个账户),利用set的自动去重和自动排序功能去除重复邮箱并对邮箱进行排序
unordered_map<int, set<string>> umt;
最后,我们将哈希表umt转变为我们可以返回的形式即可。
class Solution {
private:
vector<int> p; //该值的父辈
vector<vector<string>> res; //返回的结果
unordered_map<string,int> um;
unordered_map<int,set<string>> umt;
public:
int find(int x){ //并查集模板:寻找该值所在集合的根值
if(p[x] != x) p[x]=find(p[x]);
return p[x];
}
void merge(int x,int y){ //并查集模板:合并两个集合
int px = find(x);
int py = find(y);
if(px != py) p[py]=px;
}
vector<vector<string>> accountsMerge(vector<vector<string>>& accounts) {
int n = accounts.size();
p = vector<int>(n);
for(int i=0;i<n;i++) p[i]=i; //将每个集合的父辈初始化为自己
for(int i=0;i<n;i++){ //遍历全部邮箱
for(int j=1,m=accounts[i].size();j<m;j++){
string s = accounts[i][j];
if(um.count(s)==0) um[s]=i; //邮箱不存在则存入um中
else merge(um[s],i); //否则合并两个集合
}
}
for(int i=0;i<n;i++){ //再次遍历全部邮箱
int t = find(i); //找到该账户所在集合的根账户
for(int j=1,m=accounts[i].size();j<m;j++){
string s = accounts[i][j];
umt[t].insert(s); //将该账户的所有邮箱插入到umt中
}
}
for(auto acc: umt){ //遍历umt
vector<string> s;
s.push_back(accounts[acc.first][0]); //将账户名放入s
for(auto mail: acc.second) s.push_back(mail); //将所有邮箱放入s
res.push_back(s); //将s放入res中
}
return res;
}
};