图例简单描述一下:
代码描述:
package data.structure.unionfind;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Stack;
public class UnionSet<V> {
class Node<V> {
private V val;
public Node(V val) {
this.val = val;
}
}
private Map<V, Node<V>> nodeMap;
private Map<Node<V>, Node<V>> parents;
private Map<Node<V>, Integer> rootSizeMap;
public int getConnectedSize() {
return rootSizeMap.size();
}
public UnionSet(List<V> vals) {
nodeMap = new HashMap<>();
parents = new HashMap<>();
rootSizeMap = new HashMap<>();
for (V val : vals) {
Node<V> cur = new Node<>(val);
nodeMap.put(val, cur);
parents.put(cur,cur);// 开始自己指向自己
rootSizeMap.put(cur, 1);
}
}
public boolean unionFind(V val1, V val2) {
if (!nodeMap.containsKey(val1) || !nodeMap.containsKey(val2)) {
return false;
}
return findFather(val1) == findFather(val2);
}
public void union(V val1, V val2) {
Node<V> fatherOfVal1 = findFather(val1);
Node<V> fatherOfVal2 = findFather(val2);
if (fatherOfVal1 == fatherOfVal2) {
return;
}
Integer fatherOfVal1Size = rootSizeMap.get(fatherOfVal1);
Integer fatherOfVal2Size = rootSizeMap.get(fatherOfVal2);
// 大的节点
Node<V> bigFather = fatherOfVal1Size >= fatherOfVal2Size ? fatherOfVal1 : fatherOfVal2;
// 小的节点
Node<V> smallFather = bigFather == fatherOfVal1 ? fatherOfVal2 : fatherOfVal1;
// 小的节点指向大的节点
parents.put(smallFather, bigFather);
// 更新大的节点作为根节点维护的子元素的数量
rootSizeMap.put(bigFather, rootSizeMap.get(bigFather) + rootSizeMap.get(smallFather));
// 小的节点被吞并了,可以从Map里面剔除掉了
rootSizeMap.remove(smallFather);
}
private Node<V> findFather(V val) {
Stack<Node<V>> cacheNodeInList = new Stack<>();
Node<V> cur = nodeMap.get(val);
cacheNodeInList.push(nodeMap.get(val));
while (parents.get(cur) != null && cur != parents.get(cur)) {
Node<V> temp = parents.get(cur);
cacheNodeInList.push(temp);
cur = temp;
}
// 找到了根节点, 这里做一个优化,就是将整个链表上的节点展平, 都一度指向根节点(原先可能是个链)
while (!cacheNodeInList.isEmpty()) {
parents.put(cacheNodeInList.pop(), cur);
}
return cur;
}
}
同时可以了解一下并查集Leetcode上面的一道习题集:
用上述并查集的思想解决如下:
package leetcode.editor.cn;
import java.util.*;
import java.util.stream.Collectors;
public class P721AccountsMerge {
public static void main(String[] args) {
Solution solution = new P721AccountsMerge().new Solution();
// [["David","David0@m.co","David4@m.co","David3@m.co"],["David","David5@m.co","David5@m.co","David0@m.co"],["David","David1@m.co","David4@m.co","David0@m.co"],
// ["David","David0@m.co","David1@m.co","David3@m.co"],["David","David4@m.co","David1@m.co","David3@m.co"]]
List<List<String>> data = new ArrayList<>();
List<String> t1 = new ArrayList<>();
t1.add("David");
t1.add("David0@m.co");
t1.add("David4@m.co");
t1.add("David3@m.co");
data.add(t1);
List<String> t2 = new ArrayList<>();
t2.add("David");
t2.add("David0@m.co");
t2.add("David4@m.co");
t2.add("David3@m.co");
data.add(t2);
/*List<String> t2 = new ArrayList<>();
t2.add("David");
t2.add("David5@m.co");
t2.add("David5@m.co");
t2.add("David0@m.co");
data.add(t2);
List<String> t3 = new ArrayList<>();
t3.add("David");
t3.add("David1@m.co");
t3.add("David4@m.co");
t3.add("David0@m.co");
data.add(t3);
List<String> t4 = new ArrayList<>();
t4.add("David");
t4.add("David0@m.co");
t4.add("David1@m.co");
t4.add("David3@m.co");
data.add(t4);
List<String> t5 = new ArrayList<>();
t5.add("David");
t5.add("David4@m.co");
t5.add("David1@m.co");
t5.add("David3@m.co");
data.add(t5);*/
List<List<String>> lists = solution.accountsMerge(data);
System.out.println("Hello world" + lists);
}
//leetcode submit region begin(Prohibit modification and deletion)
class Solution {
public class ValNode<V> {
private V val;
public ValNode(V val) {
this.val = val;
}
}
private Map<String, List<Integer>> emailMap = new HashMap<>();
private Map<List<String>, ValNode<List<String>>> nodeMap = new HashMap<>();
private Map<ValNode<List<String>>, ValNode<List<String>>> parents= new HashMap<>();
private Map<ValNode<List<String>>, Set<String>> rootSizeMap= new HashMap<>();
public List<List<String>> accountsMerge(List<List<String>> accounts) {
int accountindex = 0;
for (List<String> account : accounts) {
ValNode<List<String>> valNode = new ValNode<List<String>>(account);
nodeMap.put(account, valNode);
parents.put(valNode, valNode);
for (int i = 1; i < account.size();i++) {
if (emailMap.containsKey(account.get(i))) {
List<Integer> integers = emailMap.get(account.get(i));
integers.add(accountindex);
emailMap.put(account.get(i), integers);
} else {
List<Integer> init = new ArrayList<>();
init.add(accountindex);
emailMap.put(account.get(i), init);
}
}
accountindex++;
}
nodeMap.entrySet().stream().forEach(entry -> {
rootSizeMap.put(entry.getValue(), new HashSet<>(entry.getKey().subList(1, entry.getKey().size())));
});
for (Map.Entry<String, List<Integer>> entry : emailMap.entrySet()) {
List<Integer> emailEqualList = entry.getValue();
for (int i = 0; i < emailEqualList.size() - 1; i++) {
for (int j = i + 1; j < emailEqualList.size(); j++) {
ValNode<List<String>> n1 = findFather(accounts.get(emailEqualList.get(i)));
ValNode<List<String>> n2 = findFather(accounts.get(emailEqualList.get(j)));
if (n1 != n2) {
mergerUnion(n1, n2);
}
}
}
}
// handle result
List<List<String>> list = new ArrayList<>();
for (Map.Entry<ValNode<List<String>>, Set<String>> entry : rootSizeMap.entrySet()) {
List<String> result = new ArrayList<>();
String name = entry.getKey().val.get(0);
result.add(name);
result.addAll(entry.getValue().stream().sorted().collect(Collectors.toList()));
list.add(result);
}
return list;
}
private void mergerUnion(ValNode<List<String>> n1, ValNode<List<String>> n2) {
Set<String> result1 = rootSizeMap.get(n1);
Set<String> result2 = rootSizeMap.get(n2);
ValNode<List<String>> big = result1.size() >= result2.size() ? n1 : n2;
ValNode<List<String>> small = big == n1 ? n2 : n1;
parents.put(small, big);
result1.addAll(result2);
rootSizeMap.put(big, result1);
rootSizeMap.remove(small);
}
public ValNode<List<String>> findFather(List<String> l1) {
ValNode<List<String>> n1 = nodeMap.get(l1);
Stack<ValNode<List<String>>> stack = new Stack<>();
stack.push(n1);
ValNode<List<String>> cur = n1;
while (parents.get(cur) != null && cur != parents.get(cur)) {
ValNode<List<String>> temp = parents.get(cur);
stack.push(temp);
cur = temp;
}
while (!stack.isEmpty()) {
parents.put(stack.pop(), cur);
}
return cur;
}
}
//leetcode submit region end(Prohibit modification and deletion)
}