廣度優先演算法和深度優先演算法-樹形結構(層級結構)-Java
並查集演算法
1.概念
並查集演算法,Disjoint Set;是圖論演算法的一種,用於判斷圖中是否有環。
2.原理
假設有如下一個圖,如何判斷是否有環呢?
圖中有6個頂點,分別是[0, 1, 2, 3, 4, 5];有6條邊,分別(0,1)、(1,2)、(1,3)、(2,4)、(3,4)、(2,5)【邊的兩個頂點不分順序】;
分別遍歷每一條邊,以兩端的頂點為節點分別構建一棵樹;然後把子樹合併成一棵大樹,合併過程中,如果某邊的兩頂點存在於大樹中,則表明該圖有環,否則繼續合併直到全部合併。
這裡有兩個重要的步驟:
- 子樹合併成大樹;
- 查詢子樹中是否存在合併的兩個頂點;
3.查詢與合併
合併過程中,以哪個頂點為根節點呢?
我們這裡引入一個根節點陣列,即:
// 陣列索引即節點 索引在陣列的值表示該節點的父節點; 初始化為-1
int[] parent = {-1,-1,-1,-1,-1,-1}
合併第一步:(0,1)
節點0和1的父節點均為-1,那麼表明父節點不存在,這裡認為父節點就是其本身;這種情況,兩個節點隨便哪個為父節點,這裡就假設1為0的父節點:
更新根節點陣列,則:
parent = {1,-1,-1,-1,-1,-1}
合併第二步:(1,2)
查詢節點1的父節點:parent[1] = -1,
查詢節點2的父節點:parent[2] = -1,
情況與第一步類似,這裡假設1為2的父節點:
更新根節點陣列,則:
parent = {1,-1,1,-1,-1,-1}
合併第三步:(1,3)
查詢節點1的父節點:parent[1] = -1,
查詢節點3的父節點:parent[3] = -1,
情況與第一步類似,這裡假設3為1的父節點:
更新根節點陣列,則:
parent = {1,3,1,-1,-1,-1}
合併第四步:(2,4)
查詢節點2的父節點:parent[2] = 1,
查詢節點4的父節點:parent[4] = -1,
可知,節點2的父節點為1,節點1的父節點為3,因此節點2和4的合併轉變成節點2個根根節點3和節點4的合併;又因為3的父節點則為其本身,節點4的父節點也為其本身,所以這裡假設節點3為節點4的父節點:
更新根節點陣列,則:
parent = {1,3,1,-1,3,-1}
合併第五步:(3,4)
查詢節點3的父節點:parent[3] = -1,
查詢節點4的父節點:parent[4] = 3,
節點3的父節點為其本身,節點4的父節點也是3,因此該條邊的兩個節點都在這棵樹中,則判定該圖有環。
無需進行判斷。
需要注意:
- 節點的合併,需要找到根節點;
4.程式碼
public static void main(String[] args) {
int[][] point = {{0,1}, {1,2},{1,3}, {2,4}, {3,4},{2,5}};
int[] parent = new int[point.length];
int[] rank = new int[point.length];
init(parent, rank);
int i;
for (i = 0; i <6; i++) {
int x = point[i][0];
int y = point[i][1];
if (union(x, y, parent, rank) == 0) {
System.out.println("find cycle!");
System.exit(1);
}
}
System.out.println("no cycle...");
}
// 找到根節點
static int findParent(int x, int[] parent) {
int x_root = x;
while (parent[x_root]!= -1) {
x_root = parent[x_root];
}
return x_root;
}
// 合併兩個節點 0-表示合併失敗 1-表示合併成功
static int union(int x, int y, int[] parent, int[] rank) {
int x_root = findParent(x, parent);
int y_root = findParent(y, parent);
if (x_root == y_root) {
return 0; // 擁有相同的根
} else {
if (rank[x_root] > rank[y_root]) {
parent[y_root] = x_root;
} else if (rank[x_root] < rank[y_root]) {
parent[x_root] = y_root;
} else {
parent[x_root] = y_root;
rank[y_root]++;
}
// parent[x_root] = y_root;
return 1; // 合併成功
}
}
static void init(int[] parent, int[] rank) {
for (int i =0; i < parent.length; i++) {
parent[i] = -1;
rank[i] = 0;
}
}