class Solution {
Map<Integer,Integer> unionMap = new HashMap<Integer,Integer>();
Map<String,List<Integer>> muMap = new HashMap<String,List<Integer>>();//每个邮箱地址对应的用户
public List<List<String>> accountsMerge(List<List<String>> accounts) {
//初始化并查集
//将每个账户在accounts中的下标作为其标识
for(int i = 0;i < accounts.size();i++)
unionMap.put(i,i);//将每个账户的祖先设为其自己
//将每一个邮箱地址对应的所有用户放入muMap 如果一个邮箱地址对应了多个用户 这些用户就应该进行合并
for(int i = 0;i < accounts.size(); i++)
{
List<String> list = accounts.get(i);
for(int j=1;j<list.size();j++){
if(muMap.get(list.get(j)) == null )
muMap.put(list.get(j),new ArrayList<Integer>());
List<Integer> list1 = muMap.get(list.get(j));
list1.add(i);
if(list1.size()>=2)//邮箱对应了至少两个账户时将它们合并
{
int x = list1.get(list1.size()-1);
int y = list1.get(list1.size()-2);
x = findRoot(x);
y = findRoot(y);
if(x != y)
{
if(x < y)
unionMap.put(y,x);
else
unionMap.put(x,y);
}
}
}
}
// 此时已经完成了并查集的建立
Map<Integer,Queue<String>> ans = new HashMap<Integer,Queue<String>>();
for(int i = 0;i < accounts.size(); i++)
{
int root = findRoot(i);
if(ans.get(root) == null)
ans.put(root,new PriorityQueue<String>());
Queue<String> queue = ans.get(root);
for(int j = 1;j<accounts.get(i).size();j++){
queue.offer(accounts.get(i).get(j));
}
}
List<List<String>> rans = new ArrayList<List<String>>();
for(Map.Entry<Integer,Queue<String>> map : ans.entrySet())
{
int index = map.getKey();
Queue<String> queue = map.getValue();
List<String> list = new ArrayList<>();
list.add(accounts.get(index).get(0));
while(queue.size()!=0){
String str = queue.poll();
if(str.equals(list.get(list.size()-1))==false)
list.add(str);
}
rans.add(list);
}
return rans;
}
public int findRoot(int x){
if(unionMap.get(x) == x)
return x;
else
return findRoot(unionMap.get(x));
}
}
class Solution {
public List<List<String>> accountsMerge(List<List<String>> accounts) {
//建立一个 根据邮箱地址到Index的映射
Map<String, Integer> emailToIndex = new HashMap<String, Integer>();
//简历一个 根据邮箱地址到户主名字的映射
Map<String, String> emailToName = new HashMap<String, String>();
//统计邮箱的个数
int emailsCount = 0;
//遍历给出的用户和对应的邮箱地址
for (List<String> account : accounts) {
//得到当前遍历账户下的户主名字
String name = account.get(0);
//得到当前遍历账户下List的长度
int size = account.size();
//通过此长度对接下来的邮箱进行遍历
for (int i = 1; i < size; i++) {
//得到当前i时的 邮箱地址
String email = account.get(i);
//看邮箱对应Index中是否有这个邮箱地址,保证每个邮箱的唯一性
if (!emailToIndex.containsKey(email)) {
//如果没有出现过这个邮箱,将它添加到两个对应的map中
emailToIndex.put(email, emailsCount++);
emailToName.put(email, name);
}
}
}
//此时两个Map中分别存储了每个邮箱以及它对应的Index,每个邮箱它对应的户主名字。接下来建立并查集
UnionFind uf = new UnionFind(emailsCount);
//遍历account
for (List<String> account : accounts) {
//得到当前account下第一个邮箱的地址,根据地址得到索引,把它作为根
String firstEmail = account.get(1);
int firstIndex = emailToIndex.get(firstEmail);
//得到当前account下的邮箱个数进行遍历
int size = account.size();
for (int i = 2; i < size; i++) {
//得到需要进行合并操作的邮箱地址,因为对于同一账户,他们都属于一个人,因此把他们合并
String nextEmail = account.get(i);
//根据邮箱地址从映射中取出Index
int nextIndex = emailToIndex.get(nextEmail);
//合并操作
uf.union(firstIndex, nextIndex);
}
}
//通过上面的操作,首先将同一个账户中的每个邮箱指向根邮箱,后续
//在其他的账户中遇到了相同的邮箱,在接着合并操作,此时对应根节点的个数就是总用户数
//建立一个Index对应邮箱地址的Map
Map<Integer, List<String>> indexToEmails = new HashMap<Integer, List<String>>();
//通过emailToIndex遍历邮箱地址
for (String email : emailToIndex.keySet()) {
//根据索引在并查集中得到当前邮箱地址的根节点
int index = uf.find(emailToIndex.get(email));
//判断是否存在根节点,没有就创建一个ArrrayList,有的话就获取当前的ArrayList
List<String> account = indexToEmails.getOrDefault(index, new ArrayList<String>());
//在当前的List中加入现邮箱地址,因为他们有同一个根
account.add(email);
//更新indexToEmails的邮箱列表
indexToEmails.put(index, account);
}
//创建答案表,按要求对答案进行整理
List<List<String>> merged = new ArrayList<List<String>>();
//通过indexToEmail的值(values)进行遍历
for (List<String> emails : indexToEmails.values()) {
//将emails按照题目要求进行排序
Collections.sort(emails);
//得到当前邮箱组的根,然后根据根得到用户名字
String name = emailToName.get(emails.get(0));
//创建一个account表
List<String> account = new ArrayList<String>();
//首先添加用户名字,然后用addAll将emails表整个添加到account中
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];
}
}