算法:用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]
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章