本文我用Java實現不相交集類(DisjointSet)的數據結構與算法。在這裏先解釋一下不相交集類,你可以想象成爲剛開始有N個獨立的元素,N個元素兩兩都不等價(不屬於一個同等價集合,剛開始有N個等價集合,每個等價集合都只有一個元素,且這個元素就是等價集合的根元素,可以理解爲等價集合的代表)。然後可以通過合併操作,把兩個元素變爲等價,隨着後面合併操作的量越來越大,兩個元素的合併就等於這兩個元素所代表的兩個等價集合的合併,最後合併成一個最大的等價集合。除了合併操作,還有尋找操作,就是尋找一個元素所在的等價集合的根元素。如果兩個元素尋找的根元素相等,則代表這兩個元素在同一個等價集合,即二者等價。反之則證明這兩個元素不在一個等價集合,不等價。
舉個例子說明:剛開始的時候有很多的武林人士(感覺都喜歡用武林人士來舉例子),他們都是遊離的,如何知道自己和別人是否是同一個門派的呢?你遇到了同一個門派的,你們兩個就合併,或者你認識了同一個門派的一個人,那個人已經聚團了,那你可以併到他們的集體。還有就是兩個人是兩個聚團的,然後發現是同一門派的,那麼兩個聚團就合併爲一個團。聚團裏面所有人都是等價的,都屬於一個門派:
-
比如現在有一羣人,他們剛開始都不認識,也就是兩兩都不等價,各自是一個等價集合,且等價集合的代表就是他自己:
[張無忌] [謝遜] [韋一笑] [殷天正] [空見大師] [空聞大師] [掃地神僧] [王重陽] [丘處機] [歐陽鋒] -
然後張無忌和謝遜父子相識,韋一笑和殷天正老朋友遇到了,這些朋友兩兩合併爲集合。結果如下:
[張無忌, 謝遜] [韋一笑, 殷天正] [空見大師] [空聞大師] [掃地神僧] [王重陽] [丘處機] [歐陽鋒] -
後來少林寺開大會,空見空聞兩位大師誦經唸佛,共同鑽研佛法,就遇到了,王重陽和丘處機去看熱鬧,也認識了,也是兩兩合併爲集合:
[張無忌, 謝遜] [韋一笑, 殷天正] [空見大師, 空聞大師] [掃地神僧] [王重陽, 丘處機] [歐陽鋒] -
再後來,張無忌又認了自己的外公殷天正,然後張無忌和殷天正所在的兩個集合,合併爲一個大集合:
[張無忌, 謝遜, 韋一笑, 殷天正] [空見大師, 空聞大師] [掃地神僧] [王重陽, 丘處機] [歐陽鋒] -
然後空聞大師發現,我們少林還有一個掃地神僧得嘛,以前沒發現,趕緊認識認識,然後空聞大師所在的集合與掃地神僧合併:
[張無忌, 謝遜, 韋一笑, 殷天正] [空見大師, 空聞大師, 掃地神僧] [王重陽, 丘處機] [歐陽鋒] -
合併到現在,這裏面的所有人都找到自己人了,這就是不相交集類的合併操作
我對合並的實現,則是通過一棵樹通過某些方式合併到另一棵樹,生成更大的樹進行合併操作的。一個等價集合就是一棵樹。我們可以通過查找兩個元素的樹的根節點,來判斷是否位於同一棵樹(在同一棵樹就意味着二者是等價關係)。比如謝遜和韋一笑,都位於明教這棵樹,根節點都找到張無忌了,那就是一個門派的。掃地神僧和歐陽鋒分別找根節點,發現根節點是空聞大師和歐陽鋒,不是同一個根,不在同一顆樹上,也就不等價了。
實現不相交集類(DisjointSet),我這裏有四種實現:
-
普通方法實現:合併操作先找到兩個元素所在的樹的根,然後直接把一棵樹的根,作爲另一棵樹的根的一個子節點。查找則是朝着樹的根節點方向遞歸查找,直到找到樹的根節點。這種方式,根節點的數據爲-1
-
查找優化實現:合併兩棵樹的方式與普通方法實現一樣,只是說在查找的時候,記錄下查找路徑,然後路徑上所有的等價元素,最後直接全部讓他們指向根元素,也就是樹的那一支全部打散,變爲根的子節點。以後如果需要查這些節點,找一次就找到了,提升了效率。根據算法的實現,查得越多,查的效率就越高。這種方式,根節點的數據也爲-1
-
大小依賴合併:合併兩棵樹,則是看兩棵樹誰大誰小。元素多的爲大,元素少的爲小。然後把小的樹的根節點,作爲大的樹的根節點的子節點,這種優化可以讓樹的深度不會太深,提高查找的效率。這種方式,根節點的數據爲樹的大小的負數,相當於用負數表示根節點,絕對值記錄樹的大小
-
高度依賴合併:合併兩棵樹,則是看兩棵樹誰高誰矮。把矮的樹的根節點作爲高的樹的根節點的子節點,這種優化可以讓樹的高度不會太高,也是提高了查找的效率。這種方式,根節點的數據爲樹的高度的負數,相當於用負數表示根節點,絕對值記錄樹的高度
接下來是我用Java實現不相交集類(DisjointSet)的代碼,更詳細的解釋和算法精髓都在註釋中:
- 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);
}
- 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;
}
}
- 不相交集類(並查集)直接合並的實現類(普通方法實現):
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]
- 不相交集類(並查集)查找時調整優化樹結構的實現類(查找優化實現):
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]
- 不相交集類(並查集)按樹的大小合併的實現類(大小依賴合併):
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]
- 不相交集類(並查集)按樹的高度合併的實現類(高度依賴合併):
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]