- 并查集是由一群集合构成,最开始时所有元素各自单独构成一个集合。
- 当集合中只有一个元素时,这个集合的代表节点即为该元素,该元素的father也是自己。
- 使用哈希表来存并查集中所有集合的所有元素的father信息,记为fatherMap。fatherMap中的一条记录所代表的含义是key节点的father为value节点。每个节点都有father信息。集合代表节点的father为本身。
- 另外还会存储rank信息,该信息只需要集合的代表节点记录,表示该集合中有多少个元素。记为rankMap。
- 当一个集合中有多个节点时,下层节点的father为上层节点,最上层节点的father指向自己,最上层的节点叫做这个集合的代表节点。
- 在并查集中,若要查询一个节点属于哪个集合,就是查这个节点所在集合的代表节点是什么。一个节点通过father信息找到最上面的节点,直到该节点的father是其本身停止,这个节点代表了整个集合。
- 通过一个节点找到所在集合代表节点的过程称为findFather。该过程不仅仅是单纯的查找,还会把整个查找路径压缩,会把经过的所有节点的father置为代表节点。
- 经过路径压缩之后,路径上的每个节点下次在找代表节点的时候只需要经过一次移动过程。这是整个并查集结构中最重要的优化。
- 在并查集中如何进行集合合并
- 两个集合进行合并操作时,参数是并查集中任意的两个节点,记为a,b。
- 首先找到a所在集合的代表节点记为aF,然后找到b所在集合的代表节点记为bF。
- 如果aF==bF,说明a和b本身就在一个集合里,不用合并。
- 如果aF!=bF,那么根据rankMap中存储的集合元素个数来决定,如果aFrank<bFrank,那么将aF的father置为bF,同时更新bF集合的元素个数记为bFrank+=aFrank,并且在rankMap中删除aF的rank信息;如果aFrank>bFrank,则和上述一样只不过把b换成a;如果aFrank=bFrank,那么aF和bF谁做大集合的代表都可以,但不要忘记更新rankMap。
package advan01;
import java.util.HashMap;
import java.util.List;
import java.util.Stack;
class Element<V>{
public V value;
public Element(V value) {
this.value=value;
}
}
public class UnionFindSet<V> {
public HashMap<V,Element<V>> elementMap;
public HashMap<Element<V>,Element<V>> fatherMap;
public HashMap<Element<V>,Integer> rankMap;
public UnionFindSet(List<V>list) {
elementMap=new HashMap<>();
fatherMap=new HashMap<>();
rankMap=new HashMap<>();
//初始化
for(V value:list) {
Element<V> element=new Element<V>(value);
elementMap.put(value, element);
fatherMap.put(element,element);//每个元素自称一个集合
rankMap.put(element, 1);
}
}
public Element<V> findHead(Element<V> element){
Stack<Element<V>> path=new Stack<>();
//寻找集合的代表节点
while(element!=fatherMap.get(element)) {
path.push(element);
element=fatherMap.get(element);
}
//压缩路径 对路径上经过的节点更改父节点
while(!path.isEmpty()) {
fatherMap.put(path.pop(), element);
}
return element;
}
public boolean isSameSet(V a,V b) {
if(elementMap.containsKey(a)&&elementMap.containsKey(b))
return findHead(elementMap.get(a))==findHead(elementMap.get(b));
return false;
}
public void union(V a,V b) {
if(elementMap.containsKey(a)&&elementMap.containsKey(b)) {
Element<V> aF=findHead(elementMap.get(a));
Element<V> bF=findHead(elementMap.get(b));
if(aF!=bF) {
Element<V> big=rankMap.get(aF)>=rankMap.get(bF)?aF:bF;
Element<V> small=big==aF?bF:aF;
fatherMap.put(small, big);
rankMap.put(big, rankMap.get(aF)+rankMap.get(bF));
rankMap.remove(small);
}
}
}
}