并查集及代码实现

1. 并查集

1.1 并查集的概念

并查集也是一种数据结构,主要有两个操作,集合的合并,判断两个元素是否属于同一个集合,是一种逻辑结构。

1.2 并查集的实现思路

并查集的底层实现有多重方式,可以使用数组、List、树等等,选择的依据是哪种实现方式可以让操作的复杂度小。可以使用一种特殊的树来实现并查集如图,每一个结点都有一个父结点,最后一个结点的父节点指向自己,也是这个集合的代表结点,判断两个元素是否属于同一个集合,就是判断这两个元素所在集合的代表结点是否相等,合并两个集合,让结点数少的集合,挂载结点数多的代表结点的下面。
在这里插入图片描述
具体实现时,可以使用Map,使操作更简单。

1.3 代码

public class Code_09_UnionFind {
    
    public static class Node{
		// whatever you like
    }

    public static class DisjointSets{
        //定义两个Map,fatherMap<Node, fatherNode>, 存储结点和他的父节点,sizeMap<Node, Integer>
        //存储结点和以这个结点为父节点的结点个数,并且,只有当这个结点是代表结点的时候,sizeMap中的
        //value值才有意义
        public Map<Node, Node> fatherMap;
        public Map<Node, Integer> sizeMap;

        public DisjointSets(List<Node> list){
            fatherMap = new HashMap<>();
            sizeMap = new HashMap<>();
            makeSets(list);
        }

        //初始化并查集,使用并查集的条件是,必须要知道所有元素
        //开始的时候,每个结点的父节点都是自己。
        public void makeSets(List<Node> list){
            fatherMap.clear();
            sizeMap.clear();
            for(Node node : list){
                fatherMap.put(node, node);
                sizeMap.put(node, 1);
            }
        }

        //根据一个元素,查找这个元素所在的集合的代表结点,并且,将这个集合压缩,就是从这个结点
        //到代表结点之间的所有节点的父节点,都指向代表结点
        public Node findFather(Node node){
            Node father = fatherMap.get(node);
            if(father != node){
                //这里使用的是递归,如果node的父节点不是father,就找父节点的父节点,直到找到
                //代表结点,node是一直在变的
                father = findFather(father);
            }
            //找到代表结点之后,将每个结点的父节点设为代表结点
            fatherMap.put(node, father);
            //返回father
            return father;
        }

        //判断两个元素是否属于同一个集合
        public boolean isSameSet(Node node1, Node node2){
            return findFather(node1) == findFather(node2);
        }

        //合并两个集合
        public void union(Node a, Node b){
            if(a == null || b == null) return;
            Node aFather = findFather(a);
            Node bFather = findFather(b);
            if(aFather != bFather){
                int aFatherSize = sizeMap.get(aFather);
                int bFatherSize = sizeMap.get(bFather);
                if( aFatherSize <= bFatherSize){
                    //如果a所在集合结点数少于b的,就将a的父节点设为b的父节点,a挂载b后头
                    fatherMap.put(aFather, bFather);
                    //修改个数
                    sizeMap.put(bFather, aFatherSize + bFatherSize);
                }else{
                    fatherMap.put(bFather, aFather);
                    sizeMap.put(aFather, aFatherSize + bFatherSize);
                }
            }
        }
    }
}

时间复杂度:假设有n个元素,如果查询的次数(查找代表结点)加上合并的次数的数量级达到或者超过了O(n),那么,单次查询或者单次合并,平均复杂度为O(1)。证明过程很复杂,略。

如有不足之处,欢迎指正,谢谢!

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章