并查集是一种用于管理元素所属集合的数据结构,实现为一个森林,其中每棵树表示一个集合,树中的节点表示对应集合中的元素。
顾名思义,并查集支持两种操作:
- 合并(Union):首先判断这两个元素是否属于同一集合,不属于同一集合之后,再将数量少的集合根节点指向数量多的树的集合根节点。合并两个元素所属集合【合并对应的树】
- 查询(Find):查询某个元素所属集合(查询对应的树的根节点),根节点是否相同对应两个元素是否属于同一集合
优化:在查找一个节点的根节点的时候,将经过的每个节点都和根节点直接相连,这样可以减少查询的时间复杂度。
package leetcode.HashLearning;
import java.util.HashMap;
import java.util.List;
import java.util.Stack;
public class UnionFindSet {
//把用户给的值封装一层,表示是并查集中的节点
public static class Element<V>{
public V value;
public Element(V value){
this.value=value;
}
}
public static class UnionAndFind<V>{
public HashMap<V,Element<V>> elementMap; //包装用户输入的元素
public HashMap<Element<V>,Element<V>> fatherMap; //保存当前元素和它的父元素
public HashMap<Element<V>,Integer> mapSize; //记录一个集合有多少元素【只有这个根节点保存】
public UnionAndFind(List<V> list){
elementMap=new HashMap<>();
fatherMap=new HashMap<>();
mapSize=new HashMap<>();
for (V v : list) {
Element<V> element=new Element<>(v);
elementMap.put(v,element);
fatherMap.put(element,element);
mapSize.put(element,1);
}
}
// 返回这个集合的根节点
public Element<V> findHead(Element<V> element){
Stack<Element<V>> stack=new Stack<>();
// 向上找到根节点
while (element!=fatherMap.get(element)){
stack.push(element);
element=fatherMap.get(element);
}
// 并查集改进:每次查询,把经过路径的节点和根节点直接相连
while (!stack.isEmpty()){
Element<V> element1=stack.pop();
fatherMap.put(element1,element);
}
return element;
}
//合并两个集合
public void union(Element<V> a,Element<V> b){
if (elementMap.containsKey(a) && elementMap.containsKey(b)){
Element<V> aH=findHead(a); //a集合的根节点
Element<V> bH=findHead(b); //b集合的根节点
if (aH!=bH){
Element<V> bigHead=mapSize.get(aH)>mapSize.get(bH) ? aH : bH;
Element<V> smallHead=bigHead==aH ? bH :aH;
fatherMap.put(smallHead,bigHead); //把节点少的集合连接到节点多的集合的根节点
fatherMap.remove(smallHead); //删除掉原来的smallHead根节点
}
}
}
//查询两个元素是否是同一个集合
public boolean isSameSet(Element<V> a,Element<V> b){
if (elementMap.containsKey(a) && elementMap.containsKey(b)){
return findHead(a)==findHead(b);
}
return false;
}
}
}