并查集(java详细实现)

并查集主要用于解决一些不相交集合的合并及查询问题。查询两个元素是否在同一个集合中,以及合并两个不相交集合。当然我们可以用链表等其他数据结构来实现,担当数据量特别大的时候,使用链表是非常耗时的,采用并查集结构可以大大提高合并及查询的效率。

实现原理:并查集也是一种树的结构,通过将两个元素的根节点合并从而来实现两个元素所在集合的合并,所以通过判断两个元素的根节点是否相同,就能确定这两个元素是否在同一个集合中。初始化并查集时,我们将每一个元素的根节点都指向自己。

根节点,下文成为 “ 代表节点 ” 

Java详细实现:

package unionfind;

import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Stack;

/**
 * @program: algorithm
 * @description: UnionFindSet
 * @author: LKKKK
 * @create: 2023-07-12 12:13
 **/


public class UnionFindSet2<T> {
    //并查集中的元素
    Map<T,Element2<T>> elementMap = new HashMap<>();
    //每个element对应的父节点
    Map<Element2<T>,Element2<T>> fatherMap = new HashMap();
    //以代表节点为根节点的树的节点个数
    Map<Element2<T>,Integer> sizeMap = new HashMap();

    public static class Element2<T>{
        T value;
        public Element2(T value){
            value = value;
        }
    }

    //构造方法中对并查集进行初始化
    public UnionFindSet2(List<T> list){
        for (T t : list) {
            Element2<T> tElement2 = new Element2<>(t);
            elementMap.put(t,tElement2);
            //初始化时,每个元素的父节点就是自己
            fatherMap.put(tElement2,tElement2);
            sizeMap.put(tElement2,1);
        }
    }

    //找元素的代表节点
    public Element2<T> findHead(Element2 e){
        Stack<Element2> path = new Stack<>();
        //找代表节点
        while(e != fatherMap.get(e)){
            //将在找代表节点的过程中途径的节点存到栈中
            path.push(e);
            //更新element继续找
            e = fatherMap.get(e);
        }
        //做优化(将并查集结构扁平化,避免极端情况下并查集结构退化为链表结构),将path中的节点的父节点都指向找到的代表节点,这样在以后找的时候就不用遍历fatherMap,从fatherMap中一步直接可以拿到
        while(!path.isEmpty()){
            fatherMap.put(path.pop(),e);
        }
        return e;
    }

    public boolean isSameSet(T t1,T t2){
        //判断t1,t2是不是在并查集中
        if(elementMap.containsKey(t1) && elementMap.containsKey(t2)){
            //在的话,判断他们的代表节点是不是一样
            return findHead(elementMap.get(t1)) == findHead(elementMap.get(t2));
        }
        //不在并查集中的话返回false
        return false;
    }

    public void union(T t1,T t2){
        //判断是否存在于并查集中
        if(elementMap.containsKey(t1) && elementMap.containsKey(t2)){
            //不在一个集合中的话就进行union(他们的代表节点是否相同)
            Element2<T> aHead = findHead(elementMap.get(t1));
            Element2<T> bHead = findHead(elementMap.get(t2));
            if(aHead != bHead){
                //小的合并到大的里面
                Element2<T> big = sizeMap.get(aHead) > sizeMap.get(bHead) ? aHead : bHead;
                Element2<T> small = aHead == big ? bHead : aHead;
                //合并
                //将small的代表节点设置为big
                fatherMap.put(small,big);
                //更新sizeMap中big的大小
                sizeMap.put(big,sizeMap.get(small) + sizeMap.get(big));
                //把small节点从sizeMap中删除,因为small已经不是代表节点了
                sizeMap.remove(small);
            }
        }
    }
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值