并查集的实现:

并查集的优势:
合并不同集合,判断两点是否同集合的时间复杂度都是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;
            }
        }
    }