算法:用Java实现不相交集类(DisjointSet),也叫并查集

本文我用Java实现不相交集类(DisjointSet)的数据结构与算法。在这里先解释一下不相交集类,你可以想象成为刚开始有N个独立的元素,N个元素两两都不等价(不属于一个同等价集合,刚开始有N个等价集合,每个等价集合都只有一个元素,且这个元素就是等价集合的根元素,可以理解为等价集合的代表)。然后可以通过合并操作,把两个元素变为等价,随着后面合并操作的量越来越大,两个元素的合并就等于这两个元素所代表的两个等价集合的合并,最后合并成一个最大的等价集合。除了合并操作,还有寻找操作,就是寻找一个元素所在的等价集合的根元素。如果两个元素寻找的根元素相等,则代表这两个元素在同一个等价集合,即二者等价。反之则证明这两个元素不在一个等价集合,不等价。

举个例子说明:刚开始的时候有很多的武林人士(感觉都喜欢用武林人士来举例子),他们都是游离的,如何知道自己和别人是否是同一个门派的呢?你遇到了同一个门派的,你们两个就合并,或者你认识了同一个门派的一个人,那个人已经聚团了,那你可以并到他们的集体。还有就是两个人是两个聚团的,然后发现是同一门派的,那么两个聚团就合并为一个团。聚团里面所有人都是等价的,都属于一个门派:

  1. 比如现在有一群人,他们刚开始都不认识,也就是两两都不等价,各自是一个等价集合,且等价集合的代表就是他自己:
    [张无忌] [谢逊] [韦一笑] [殷天正] [空见大师] [空闻大师] [扫地神僧] [王重阳] [丘处机] [欧阳锋]

  2. 然后张无忌和谢逊父子相识,韦一笑和殷天正老朋友遇到了,这些朋友两两合并为集合。结果如下:
    [张无忌, 谢逊] [韦一笑, 殷天正] [空见大师] [空闻大师] [扫地神僧] [王重阳] [丘处机] [欧阳锋]

  3. 后来少林寺开大会,空见空闻两位大师诵经念佛,共同钻研佛法,就遇到了,王重阳和丘处机去看热闹,也认识了,也是两两合并为集合:
    [张无忌, 谢逊] [韦一笑, 殷天正] [空见大师, 空闻大师] [扫地神僧] [王重阳, 丘处机] [欧阳锋]

  4. 再后来,张无忌又认了自己的外公殷天正,然后张无忌和殷天正所在的两个集合,合并为一个大集合:
    [张无忌, 谢逊, 韦一笑, 殷天正] [空见大师, 空闻大师] [扫地神僧] [王重阳, 丘处机] [欧阳锋]

  5. 然后空闻大师发现,我们少林还有一个扫地神僧得嘛,以前没发现,赶紧认识认识,然后空闻大师所在的集合与扫地神僧合并:
    [张无忌, 谢逊, 韦一笑, 殷天正] [空见大师, 空闻大师, 扫地神僧] [王重阳, 丘处机] [欧阳锋]

  6. 合并到现在,这里面的所有人都找到自己人了,这就是不相交集类的合并操作

我对合并的实现,则是通过一棵树通过某些方式合并到另一棵树,生成更大的树进行合并操作的。一个等价集合就是一棵树。我们可以通过查找两个元素的树的根节点,来判断是否位于同一棵树(在同一棵树就意味着二者是等价关系)。比如谢逊和韦一笑,都位于明教这棵树,根节点都找到张无忌了,那就是一个门派的。扫地神僧和欧阳锋分别找根节点,发现根节点是空闻大师和欧阳锋,不是同一个根,不在同一颗树上,也就不等价了。

实现不相交集类(DisjointSet),我这里有四种实现:

  1. 普通方法实现:合并操作先找到两个元素所在的树的根,然后直接把一棵树的根,作为另一棵树的根的一个子节点。查找则是朝着树的根节点方向递归查找,直到找到树的根节点。这种方式,根节点的数据为-1

  2. 查找优化实现:合并两棵树的方式与普通方法实现一样,只是说在查找的时候,记录下查找路径,然后路径上所有的等价元素,最后直接全部让他们指向根元素,也就是树的那一支全部打散,变为根的子节点。以后如果需要查这些节点,找一次就找到了,提升了效率。根据算法的实现,查得越多,查的效率就越高。这种方式,根节点的数据也为-1

  3. 大小依赖合并:合并两棵树,则是看两棵树谁大谁小。元素多的为大,元素少的为小。然后把小的树的根节点,作为大的树的根节点的子节点,这种优化可以让树的深度不会太深,提高查找的效率。这种方式,根节点的数据为树的大小的负数,相当于用负数表示根节点,绝对值记录树的大小

  4. 高度依赖合并:合并两棵树,则是看两棵树谁高谁矮。把矮的树的根节点作为高的树的根节点的子节点,这种优化可以让树的高度不会太高,也是提高了查找的效率。这种方式,根节点的数据为树的高度的负数,相当于用负数表示根节点,绝对值记录树的高度

接下来是我用Java实现不相交集类(DisjointSet)的代码,更详细的解释和算法精髓都在注释中:

  1. DisjointSet接口类,所有不相交集类的实例都要实现该接口里面的操作方法(find查找根元素,union合并元素,size返回元素个数):
/**
 2. @author LiYang
 3. @InterfaceName DisjointSet
 4. @Description 不相交集类(并查集)的公用接口
 5. @date 2019/11/14 10:42
 */
public interface DisjointSet {
    
    //不相交集类(并查集)的元素个数
    int size();

    //查找元素在不相交集类(并查集)的根元素
    //注意,这里都是操作数组下标,而每个下标
    //可以映射到具体的事物
    int find(int elementIndex);
    
    //不相交集类(并查集)的两个元素的合并操作
    //注意,这里也是下标的合并,也可映射具体事物
    void union(int elementIndex1, int elementIndex2);
    
}
  1. DisjointSetUtil不相交集工具类,可以统计合并后的所有根节点,以及所在等价集合里面的所有等价元素:
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;

/**
 1. @author LiYang
 2. @ClassName DisjointSetUtil
 3. @Description 不相交集类(并查集)的工具类
 4. @date 2019/11/14 10:40
 */
public class DisjointSetUtil {

    /**
     * 返回不相交集类(并查集)所有的根,以及对应的所有的等价元素
     * @param disjointSet 不相交集类(并查集)
     * @return Map<根,根的所有等价元素>
     */
    public static Map<Integer, Set<Integer>> arrangeDisjointSet(DisjointSet disjointSet) {
        //装结果的数据Map
        Map<Integer, Set<Integer>> result = new HashMap<>();

        //将不相交集类(并查集)所有元素遍历
        for (int i = 0; i < disjointSet.size(); i++) {
            
            //先找到当前元素的根元素
            int root = disjointSet.find(i);
            
            //如果数据Map中有根统计结果了
            if (result.containsKey(root)) {
                //拿到该根的等价Set,装入当前元素
                result.get(root).add(i);
                
            //如果数据Map中没有根统计结果
            } else {
                //创建当前根的等价Set
                Set<Integer> equivalentSet = new HashSet<>();
                
                //加入当前元素
                equivalentSet.add(i);
                
                //将当前根和等价Set放入数据Map
                result.put(root, equivalentSet);
            }
        }
        
        //统计完成,返回根和所有等价元素的统计结果Map
        return result;
    }
    
}
  1. 不相交集类(并查集)直接合并的实现类(普通方法实现):
import java.util.Map;
import java.util.Set;

/**
 * @author LiYang
 * @ClassName DisjointSetUnionDirectly
 * @Description 不相交集类(并查集)直接合并的实现类
 * @date 2019/11/14 10:29
 */
public class DisjointSetUnionDirectly implements DisjointSet {

    //不相交集类(并查集)的元素数组
    private int[] elements;

    /**
     * 不相交集类(并查集)的构造方法,入参元素个数
     * @param elementSize 元素个数
     */
    public DisjointSetUnionDirectly(int elementSize) {
        if (elementSize <= 0) {
            throw new IllegalArgumentException("不相交集合的元素个数要大于零");
        }
        
        //实例化化不相交集类(并查集)的元素数组
        this.elements = new int[elementSize];

        //初始化元素数组都为-1
        for (int i = 0; i < elements.length; i++) {
            elements[i] = -1;
        }
    }

    /**
     * 查询不相交集类(并查集)的元素个数
     * @return 元素个数
     */
    @Override
    public int size() {
        return elements.length;
    }

    /**
     * 查询不相交集类(并查集)的某个元素的根元素
     * 输入的是下标查,如果两个元素的根元素相同,
     * 则这两个元素就是等价的。实际中还会有一个
     * 与elements等长的数组,装的是真实的元素,
     * elements只是相当于代号,记录等价关系,
     * 二者通过下标,来映射真实元素
     * @param elementIndex 待查询的元素下标
     * @return 该元素的根元素
     */
    @Override
    public int find(int elementIndex) {
        //如果记录小于0,那就是根
        if (elements[elementIndex] < 0) {
            //返回根元素
            return elementIndex;
            
        //如果记录不小于0,那还不是根,
        //是等价森林中的上一个节点
        } else {
            //递归向上继续寻找根
            return find(elements[elementIndex]);
        }
    }

    /**
     * 将不相交集类(并查集)的两个元素进行合并操作
     * 注意,两个元素合并,代表这两个元素所在的两个
     * 等价集合,全部变成一个大的等价集合。如果这
     * 两个元素本来就等价,则不进行合并操作。
     * 注意,这里同样是入参下标,下标映射真实元素
     * @param elementIndex1 元素下标1
     * @param elementIndex2 元素下标2
     */
    @Override
    public void union(int elementIndex1, int elementIndex2) {
        //找到这两个元素的根
        int root1 = find(elementIndex1);
        int root2 = find(elementIndex2);
        
        //如果两个元素的根相同,那这两个元素本来就等价
        if (root1 == root2) {
            //既然等价,那就不操作
            return;
        }
        
        //如果不等价,则将第一个元素的树的根,合并到第二个
        //元素的树的根上,后者的根的一个子树即为前者
        elements[root1] = root2;
    }

    /**
     * 相交集类(并查集)直接合并的实现类测试
     * @param args
     */
    public static void main(String[] args) {
        //实例化相交集类(并查集)直接合并的实现类,并初始化元素个数为8个
        DisjointSetUnionDirectly disjointSetUnionDirectly = new DisjointSetUnionDirectly(8);
        
        //进行一系列的合并操作,其中包含已等价的合并
        disjointSetUnionDirectly.union(2,3);
        disjointSetUnionDirectly.union(5,6);
        disjointSetUnionDirectly.union(3,5);
        disjointSetUnionDirectly.union(0,7);
        disjointSetUnionDirectly.union(2,6);

        //获取该相交集类(并查集)实现类的等价记录Map
        Map<Integer, Set<Integer>> equivalentMap = DisjointSetUtil.arrangeDisjointSet(disjointSetUnionDirectly);

        System.out.println("不相交集类(并查集)直接合并的实现类测试结果:");
        
        //输出等价记录Map的内容,进行测试验证
        for (Map.Entry<Integer, Set<Integer>> item : equivalentMap.entrySet()) {
            System.out.println(String.format("root:%d, elements:%s", item.getKey(), item.getValue().toString()));
        }
    }
    
}

运行main方法的测试用例,控制台输出如下,测试通过:

不相交集类(并查集)直接合并的实现类测试结果:
root:1, elements:[1]
root:4, elements:[4]
root:6, elements:[2, 3, 5, 6]
root:7, elements:[0, 7]
  1. 不相交集类(并查集)查找时调整优化树结构的实现类(查找优化实现):
import java.util.HashSet;
import java.util.Map;
import java.util.Set;

/**
 * @author LiYang
 * @ClassName DisjointSetFindAdjust
 * @Description 不相交集类(并查集)查找时调整优化树结构的实现类
 * @date 2019/11/14 11:37
 */
public class DisjointSetFindAdjust implements DisjointSet {

    //不相交集类(并查集)的元素数组
    private int[] elements;

    /**
     * 不相交集类(并查集)的构造方法,入参元素个数
     * @param elementSize 元素个数
     */
    public DisjointSetFindAdjust(int elementSize) {
        if (elementSize <= 0) {
            throw new IllegalArgumentException("不相交集合的元素个数要大于零");
        }

        //实例化化不相交集类(并查集)的元素数组
        this.elements = new int[elementSize];

        //初始化元素数组都为-1
        for (int i = 0; i < elements.length; i++) {
            elements[i] = -1;
        }
    }

    /**
     * 查询不相交集类(并查集)的元素个数
     * @return 元素个数
     */
    @Override
    public int size() {
        return elements.length;
    }

    /**
     * 查询不相交集类(并查集)的某个元素的根元素
     * 输入的是下标查,如果两个元素的根元素相同,
     * 则这两个元素就是等价的。实际中还会有一个
     * 与elements等长的数组,装的是真实的元素,
     * elements只是相当于代号,记录等价关系,
     * 二者通过下标,来映射真实元素
     * 注意,此实现类的find在往根节点方向寻找的路径
     * 中的所有元素会被记录,最后全部直接指向根元素
     * @param elementIndex 待查询的元素下标
     * @return 该元素的根元素
     */
    @Override
    public int find(int elementIndex) {
        //盛放find寻根路上所有的等价节点
        Set<Integer> equivalentSet = new HashSet<>();
        
        //如果还没找到根
        while (elements[elementIndex] >= 0) {
            //将当前寻路的元素放入等价Set中
            equivalentSet.add(elementIndex);
            
            //更新elementIndex
            elementIndex = elements[elementIndex];
        }
        
        //while循环结束,也就是找到根了,当前elementIndex就是根
        //将集合里面的所有等价元素,全部合并到根上
        for (Integer equivalentElement : equivalentSet) {
            //这下根就有很多子节点了
            //随着find的不断调用,树的平均高度越来越接近2
            //然后以后的find的效率就会越来越高了
            elements[equivalentElement] = elementIndex;
        }
        
        //调整完毕后,返回找到的根节点
        return elementIndex;
    }

    /**
     * 将不相交集类(并查集)的两个元素进行合并操作
     * 注意,两个元素合并,代表这两个元素所在的两个
     * 等价集合,全部变成一个大的等价集合。如果这
     * 两个元素本来就等价,则不进行合并操作。
     * 注意,这里同样是入参下标,下标映射真实元素
     * 此实现类沿用了直接合并的方式,指望find方法
     * 能够在查找的过程中不断调整和优化查找效率
     * @param elementIndex1 元素下标1
     * @param elementIndex2 元素下标2
     */
    @Override
    public void union(int elementIndex1, int elementIndex2) {
        //找到这两个元素的根
        int root1 = find(elementIndex1);
        int root2 = find(elementIndex2);

        //如果两个元素的根相同,那这两个元素本来就等价
        if (root1 == root2) {
            //既然等价,那就不操作
            return;
        }

        //如果不等价,则将第一个元素的树的根,合并到第二个
        //元素的树的根上,后者的根的一个子树即为前者
        elements[root1] = root2;
    }

    /**
     * 不相交集类(并查集)查找时调整优化树结构的实现类测试
     * @param args
     */
    public static void main(String[] args) {
        //实例化不相交集类(并查集)查找时调整优化树结构的实现类,并初始化元素个数为8个
        DisjointSetFindAdjust disjointSetFindAdjust = new DisjointSetFindAdjust(8);

        //进行一系列的合并操作,其中包含已等价的合并
        disjointSetFindAdjust.union(2,3);
        disjointSetFindAdjust.union(5,6);
        disjointSetFindAdjust.union(3,5);
        disjointSetFindAdjust.union(0,7);
        disjointSetFindAdjust.union(2,6);

        //获取该相交集类(并查集)实现类的等价记录Map
        Map<Integer, Set<Integer>> equivalentMap = DisjointSetUtil.arrangeDisjointSet(disjointSetFindAdjust);

        System.out.println("不相交集类(并查集)查找时调整优化树结构的实现测试结果:");

        //输出等价记录Map的内容,进行测试验证
        for (Map.Entry<Integer, Set<Integer>> item : equivalentMap.entrySet()) {
            System.out.println(String.format("root:%d, elements:%s", item.getKey(), item.getValue().toString()));
        }
    }
    
}

运行main方法的测试用例,控制台输出如下,测试通过:

不相交集类(并查集)查找时调整优化树结构的实现测试结果:
root:1, elements:[1]
root:4, elements:[4]
root:6, elements:[2, 3, 5, 6]
root:7, elements:[0, 7]
  1. 不相交集类(并查集)按树的大小合并的实现类(大小依赖合并):
import java.util.Map;
import java.util.Set;

/**
 * @author LiYang
 * @ClassName DisjointSetUnionBySize
 * @Description 不相交集类(并查集)按树的大小合并的实现类
 * @date 2019/11/14 10:54
 */
public class DisjointSetUnionBySize implements DisjointSet {

    //不相交集类(并查集)的元素数组
    private int[] elements;

    /**
     * 不相交集类(并查集)的构造方法,入参元素个数
     * @param elementSize 元素个数
     */
    public DisjointSetUnionBySize(int elementSize) {
        if (elementSize <= 0) {
            throw new IllegalArgumentException("不相交集合的元素个数要大于零");
        }

        //实例化化不相交集类(并查集)的元素数组
        this.elements = new int[elementSize];

        //初始化元素的大小都为-1(如果是根,值就是负数,等价元素组成的树
        //的大小是多少,则根元素就是负几)
        for (int i = 0; i < elements.length; i++) {
            elements[i] = -1;
        }
    }

    /**
     * 查询不相交集类(并查集)的元素个数
     * @return 元素个数
     */
    @Override
    public int size() {
        return elements.length;
    }
    
    /**
     * 查询不相交集类(并查集)的某个元素的根元素
     * 输入的是下标查,如果两个元素的根元素相同,
     * 则这两个元素就是等价的。实际中还会有一个
     * 与elements等长的数组,装的是真实的元素,
     * elements只是相当于代号,记录等价关系,
     * 二者通过下标,来映射真实元素
     * @param elementIndex 待查询的元素下标
     * @return 该元素的根元素
     */
    @Override
    public int find(int elementIndex) {
        //如果记录小于0,那就是根
        if (elements[elementIndex] < 0) {
            //返回根元素
            return elementIndex;

        //如果记录不小于0,那还不是根,
        //是等价森林中的上一个节点
        } else {
            //递归向上继续寻找根
            return find(elements[elementIndex]);
        }
    }

    /**
     * 将不相交集类(并查集)的两个元素进行合并操作
     * 注意,两个元素合并,代表这两个元素所在的两个
     * 等价集合,全部变成一个大的等价集合。如果这
     * 两个元素本来就等价,则不进行合并操作。
     * 注意,这里同样是入参下标,下标映射真实元素
     * 此实现类,根据树的大小来决定谁合并到谁上面,
     * 小的树的根节点,会作为大的树的根节点的子节点
     * @param elementIndex1 元素下标1
     * @param elementIndex2 元素下标2
     */
    @Override
    public void union(int elementIndex1, int elementIndex2) {
        int root1 = find(elementIndex1);
        int root2 = find(elementIndex2);

        //如果两个元素本属于一个集合
        if (root1 == root2) {
            //不作处理
            return;
        }
        
        //比大小:如果root1比root2的树要大
        if (elements[root1] < elements[root2]) {
            //合并后,root1的高度为二者之和
            elements[root1] = elements[root1] + elements[root2];
            
            //将较小的root2合并到较大的root1上
            elements[root2] = root1;
        
        //比大小:如果root2的树大于等于root1的树
        } else {
            //合并后,root2的高度为二者之和
            elements[root2] = elements[root2] + elements[root1];
            
            //将较小或相等的root1合并到较大或相等的root2上
            elements[root1] = root2;
        }
    }

    /**
     * 不相交集类(并查集)按树的大小合并的实现类测试
     * @param args
     */
    public static void main(String[] args) {
        //实例化不相交集类(并查集)按树的大小合并的实现类,并初始化元素个数为8个
        DisjointSetUnionBySize disjointSetUnionBySize = new DisjointSetUnionBySize(8);

        //进行一系列的合并操作,其中包含已等价的合并
        disjointSetUnionBySize.union(2,3);
        disjointSetUnionBySize.union(5,6);
        disjointSetUnionBySize.union(3,5);
        disjointSetUnionBySize.union(0,7);
        disjointSetUnionBySize.union(2,6);

        //获取该相交集类(并查集)实现类的等价记录Map
        Map<Integer, Set<Integer>> equivalentMap = DisjointSetUtil.arrangeDisjointSet(disjointSetUnionBySize);

        System.out.println("不相交集类(并查集)按树的大小合并的实现测试结果:");

        //输出等价记录Map的内容,进行测试验证
        for (Map.Entry<Integer, Set<Integer>> item : equivalentMap.entrySet()) {
            System.out.println(String.format("root:%d, elements:%s", item.getKey(), item.getValue().toString()));
        }
    }
    
}

运行main方法的测试用例,控制台输出如下,测试通过:

不相交集类(并查集)按树的大小合并的实现测试结果:
root:1, elements:[1]
root:4, elements:[4]
root:6, elements:[2, 3, 5, 6]
root:7, elements:[0, 7]
  1. 不相交集类(并查集)按树的高度合并的实现类(高度依赖合并):
import java.util.Map;
import java.util.Set;

/**
 * @author LiYang
 * @ClassName DisjointSetUnionByHeight
 * @Description 不相交集类(并查集)按树的高度合并的实现类
 * @date 2019/11/14 11:21
 */
public class DisjointSetUnionByHeight implements DisjointSet {

    //不相交集类(并查集)的元素数组
    private int[] elements;

    /**
     * 不相交集类(并查集)的构造方法,入参元素个数
     * @param elementSize 元素个数
     */
    public DisjointSetUnionByHeight(int elementSize) {
        if (elementSize <= 0) {
            throw new IllegalArgumentException("不相交集合的元素个数要大于零");
        }

        //实例化化不相交集类(并查集)的元素数组
        this.elements = new int[elementSize];

        //初始化元素的高度都为-1(如果是根,值就是负数,等价元素组成的树
        //的高度是多少,则根元素就是负几)
        for (int i = 0; i < elements.length; i++) {
            elements[i] = -1;
        }
    }

    /**
     * 查询不相交集类(并查集)的元素个数
     * @return 元素个数
     */
    @Override
    public int size() {
        return elements.length;
    }

    /**
     * 查询不相交集类(并查集)的某个元素的根元素
     * 输入的是下标查,如果两个元素的根元素相同,
     * 则这两个元素就是等价的。实际中还会有一个
     * 与elements等长的数组,装的是真实的元素,
     * elements只是相当于代号,记录等价关系,
     * 二者通过下标,来映射真实元素
     * @param elementIndex 待查询的元素下标
     * @return 该元素的根元素
     */
    @Override
    public int find(int elementIndex) {
        //如果记录小于0,那就是根
        if (elements[elementIndex] < 0) {
            //返回根元素
            return elementIndex;

        //如果记录不小于0,那还不是根,
        //是等价森林中的上一个节点
        } else {
            //递归向上继续寻找根
            return find(elements[elementIndex]);
        }
    }

    /**
     * 将不相交集类(并查集)的两个元素进行合并操作
     * 注意,两个元素合并,代表这两个元素所在的两个
     * 等价集合,全部变成一个大的等价集合。如果这
     * 两个元素本来就等价,则不进行合并操作。
     * 注意,这里同样是入参下标,下标映射真实元素
     * 此实现类,根据树的高度来决定谁合并到谁上面,
     * 矮的树的根节点,会作为大的树的根节点的子节点
     * @param elementIndex1 元素下标1
     * @param elementIndex2 元素下标2
     */
    @Override
    public void union(int elementIndex1, int elementIndex2) {
        int root1 = find(elementIndex1);
        int root2 = find(elementIndex2);

        //如果两个元素本属于一个集合
        if (root1 == root2) {
            //不作处理
            return;
        }

        //比高度:如果root1比root2的树要高
        if (elements[root1] < elements[root2]) {
            //将较矮的root2合并到较高的root1上
            elements[root2] = root1;
        
        //比高度:如果root2比root1的树要高
        } else if (elements[root2] < elements[root1]) {
            //将较矮的root1合并到较高的root2上
            elements[root1] = root2;
        
        //比高度:如果root1和root2一样高
        } else {
            //将root1合并到root2上
            elements[root1] = root2;
            
            //root2的高度增加1
            root2 --;
        }
    }

    /**
     * 不相交集类(并查集)按树的高度合并的实现类测试
     * @param args
     */
    public static void main(String[] args) {
        //实例化不相交集类(并查集)按树的高度合并的实现类,并初始化元素个数为8个
        DisjointSetUnionByHeight disjointSetUnionByHeight = new DisjointSetUnionByHeight(8);

        //进行一系列的合并操作,其中包含已等价的合并
        disjointSetUnionByHeight.union(2,3);
        disjointSetUnionByHeight.union(5,6);
        disjointSetUnionByHeight.union(3,5);
        disjointSetUnionByHeight.union(0,7);
        disjointSetUnionByHeight.union(2,6);

        //获取该相交集类(并查集)实现类的等价记录Map
        Map<Integer, Set<Integer>> equivalentMap = DisjointSetUtil.arrangeDisjointSet(disjointSetUnionByHeight);

        System.out.println("不相交集类(并查集)按树的高度合并的实现测试结果:");

        //输出等价记录Map的内容,进行测试验证
        for (Map.Entry<Integer, Set<Integer>> item : equivalentMap.entrySet()) {
            System.out.println(String.format("root:%d, elements:%s", item.getKey(), item.getValue().toString()));
        }
    }
    
}

运行main方法的测试用例,控制台输出如下,测试通过:

不相交集类(并查集)按树的高度合并的实现测试结果:
root:1, elements:[1]
root:4, elements:[4]
root:6, elements:[2, 3, 5, 6]
root:7, elements:[0, 7]
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章