并查集【原理&代码实现】

并查集是一种用于管理元素所属集合的数据结构,实现为一个森林,其中每棵树表示一个集合,树中的节点表示对应集合中的元素。

顾名思义,并查集支持两种操作:

  • 合并(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;
        }
    }
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值