题目
基础知识准备-并查集
- 查找并返回所在集合的根节点:find():
注意:
根节点特性是:father[i]==i; 每次查找时进行路径压缩,保证集合树的高度不大于2;
int find(int i) { //寻找根节点
int f = i;
while (father[f] != f) //当该结点不是根节点时
f = father[f]; //上诉到父结点
//路径压缩
while (i != f) {
int tmp = father[i];
father[i] = f;
i = tmp;
}
return f; //根节点
}
- 合并两个元素所在集合:combine():
void combine(int i, int j) { //把j加入i集合
int i_f = find(i); //查找所在集合的根节点
int j_f = find(j);
if (i_f != j_f) { //根节点不同时,不在一个集合,才合并
father[j_f] = i_f;
}
}
题目思路
本题的重点是抽象、映射、确定哪里需要用到并查集。
- 需要用到并查集的是用户编号,即accounts中的第一维编号(不能用用户名,可能出现重复),初始时,每个用户的父节点是自己:father[i]=i。
- 遍历每个用户的每个邮箱:若邮箱mail第一次出现,记录所属的用户编号i,用unorder_map<string,int>mail_userid保存;若邮箱重复出现,合并当前用户i和邮箱第一次出现的用户mail_userid[mail],这里才用到了并查集。
- 然后,查看每个用户的父结点,若父结点不是自己,说明自己的邮箱和别的用户重复,把自己旗下的邮箱归入父结点门下。
C++代码(注释很详细~)
class Solution {
public:
int father[1001]; //用户-》父用户的并查集映射
int find(int i) { //寻找根节点
int f = i;
while (father[f] != f) //当该结点不是根节点时
f = father[f]; //上诉到父结点
//路径压缩
while (i != f) {
int tmp = father[i];
father[i] = f;
i = tmp;
}
return f; //根节点
}
void combine(int i, int j) { //把j加入i集合
int i_f = find(i);
int j_f = find(j);
if (i_f != j_f) { //根节点不同时,不在一个集合,则合并
father[j_f] = i_f;
}
}
vector<vector<string>> accountsMerge(vector<vector<string>>& accounts) {
int n = accounts.size();
for (int i = 0; i < n; ++i)father[i] = i;//初始时,父亲是自己
unordered_map<string, int>mail_user; //邮箱,所属用户编号
for (int i = 0; i < n; ++i) {
int len = accounts[i].size();
for (int j = 1; j < len; ++j) {
if (!mail_user.count(accounts[i][j])) //邮箱第一次出现
mail_user[accounts[i][j]] = i;//<邮箱,用户编号>
else combine(mail_user[accounts[i][j]], i); //把i用户的父用户设为第一次使用该邮箱的用户
}
}
//把邮箱归于真正所属的父用户中(并set排序)
unordered_map<int, set<string> >user_mails;//父用户编号,所含邮箱
for (int i = 0; i < n; ++i) {
int f_idx = find(i); //用户所属的 父用户编号
for (int j = 1; j < accounts[i].size(); ++j)
user_mails[f_idx].insert(accounts[i][j]); //把邮箱加入 所属父用户
}
//转化为规定形式 返回
vector<vector<string>>ret;
for (auto it : user_mails) {
vector<string>tmp = { accounts[it.first][0] };//先加入用户名
for (string mail : it.second)
tmp.push_back(mail); //加入邮箱
ret.push_back(tmp);
}
return ret;
}
};