并查集的实现:
并查集的优势:
合并不同集合,判断两点是否同集合的时间复杂度都是O(1)
并不是真正意义上的O(1)
但是调用findHead()方法的次数越多,平均时间复杂度一样
改进:
由于并查集机制的原因,合并集合时,小的集合的头要挂在大的集合的尾部,
但是如果集合元素过多,本质会变成很长的链表,节点查找头部的时间会边长,所
以findhead()改进方式是,查找到头节点的同时,将当前路径遇到的所有节点
直接挂到头节点,来减小链表的长度
///节点类
class Element<v>{
v val;
public Element(v v){
this.val=v;
}
}
public static class Union<v>{
//记录每个节点的父节点map
public Map<Element<v>,Element<v>> elementMap=new HashMap<>();
//记录所有节点
public Map<v,Element<v>> getMap=new HashMap<>();
//记录当前集合的大小
public Map<Element<v>,Integer> sizeMap=new HashMap<>();
public void initElement(ArrayList<v> list){
for (v in:list){//初始化,使每一个节点都生成一个集合
//集合的头部是各个节点
Element<v> element=new Element<v>(in);
sizeMap.put(element,1);//记录以各个节点为首的集合的大小
elementMap.put(element,element);//让各个节点指向自己
getMap.put(in,element);//方便通过值获取element对象的map
}
}
//合并不同的集合
public void Uniondifun(v v1,v v2){
if (getMap.containsKey(v1)&&getMap.containsKey(v2)){
if (!isUnion(v1,v2)){
Element<v> head1=findHead(v1);
Element<v> head2=findHead(v2);
//重定向head1head2,大的集合给max,小的给min
//重定向原因是,集合合并的原则是,小的集合要挂在大的集合下面
Element<v> max=sizeMap.get(head1)>sizeMap.get(head2)?head1:head2;
Element<v> min=max==head1?head2:head1;
elementMap.put(min,max);
sizeMap.put(max,sizeMap.get(head1)+sizeMap.get(head2));
sizeMap.remove(min);
}
}
}
//判断两点是否在同一集合
//条件是,两个点的head相同,则在一个集合中,否则不是
public boolean isUnion(v v1,v v2){
if (getMap.containsKey(v1)&&getMap.containsKey(v2)){
return findHead(v1)==findHead(v2);
}
return false;
}
//向上查找指定节点的集合头部
Element<v> findHead(v v){
if (getMap.containsKey(v)){
Stack<Element<v>> stack=new Stack<>();
Element<v> element=getMap.get(v);
while (element!=elementMap.get(element)){
stack.push(element);
element=elementMap.get(element);
}
//此处包含优化
while (!stack.isEmpty()){
elementMap.put(stack.pop(),element);
}
return element;
}else {
return null;
}
}
}
浙公网安备 33010602011771号