【并查集】LeetCode 721.账户合并
题解
对于\(vector<vector<string>> accounts\),我们定义\(accounts[i]\)为一个列表项。对于\(accounts\)中的一条列表项,第一个元素代表用户名称,其余元素均代表该用户所拥有的邮箱地址。如下图所示,是一个具有三条列表项的\(accounts\):

对于一个用户名称,其拥有的邮箱地址,不妨视为一个不包含重复元素的集合。当且仅当列表项之间的邮箱地址集合有交集时,可以合并列表项。
不妨假设\(n = accounts.size()\),那么本质就是问这\(n\)条列表项在所有具有交集的列表项进行合并后的集合信息。
可以用并查集维护\(n\)个不同的集合。
对于第\(i(0 \leq i \leq n-1)\)条列表项,易知该条列表项的邮箱地址都曾出现于第\(i\)个邮箱地址集合中。
那么不妨用\(map<string, vector<int>>\)维护邮箱地址出现过的集合序号,结果只需要将全部集合序号所指的集合合并为一个集合,最后的结果就是所求答案。
例如上述给出的图片,其集合序号信息如下:
邮箱地址1
1
邮箱地址2
1 2
邮箱地址3
1
邮箱地址4
2
邮箱地址5
2
邮箱地址6
3
由邮箱地址2 {1, 2},可知集合1和集合2是具有交集的,可以合并,并且可以证明,除此之外再无其他集合有交集。进行合并后,集合信息为:
- 1(2) {邮箱地址1, 邮箱地址2, 邮箱地址3, 邮箱地址4, 邮箱地址5};
- 3 {邮箱地址6}。
参考代码
class Solution {
public:
void merge(vector<int> &bcj, int a, int b) {
bcj[find(bcj, a)] = find(bcj, b);
}
int find(vector<int> &bcj, int x) {
while (x != bcj[x]) {
return bcj[x] = find(bcj, bcj[x]);
}
return x;
}
vector<vector<string>> accountsMerge(vector<vector<string>>& accounts) {
vector<vector<string>> ans;
map<string, vector<int>> mp;
int n = accounts.size();
vector<int> bcj(n);
iota(bcj.begin(), bcj.end(), 0);
for (int i = 0; i < n; ++ i) {
for (int j = 1; j < accounts[i].size(); ++ j) {
mp[accounts[i][j]].emplace_back(i);
}
}
for (auto &it: mp) {
vector<int> &v = it.second;
int len = v.size();
for (int i = 1; i < len; ++ i) {
merge(bcj, v[0], v[i]);
}
}
map<int, set<string>> res;
for (int i = 0; i < n; ++ i) {
int anc = find(bcj, i);
for (int j = 1; j < accounts[i].size(); ++ j) {
res[anc].insert(accounts[i][j]);
}
}
for (auto &it: res) {
vector<string> vec;
vec.emplace_back(accounts[it.first][0]);
vec.insert(vec.end(), it.second.begin(), it.second.end());
ans.emplace_back(vec);
}
return ans;
}
};
浙公网安备 33010602011771号