1. 程式人生 > 其它 >廣度優先演算法和深度優先演算法-樹形結構(層級結構)-Java

廣度優先演算法和深度優先演算法-樹形結構(層級結構)-Java

技術標籤:演算法並查集

並查集演算法

1.概念

並查集演算法,Disjoint Set;是圖論演算法的一種,用於判斷圖中是否有環。

2.原理

假設有如下一個圖,如何判斷是否有環呢?
圖1
圖中有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的父節點:
圖1
更新根節點陣列,則:

parent = {1,-1,-1,-1,-1,-1}
合併第二步:(1,2)

查詢節點1的父節點:parent[1] = -1,
查詢節點2的父節點:parent[2] = -1,
情況與第一步類似,這裡假設1為2的父節點:

圖2
更新根節點陣列,則:

parent = {1,-1,1,-1,-1,-1}
合併第三步:(1,3)

查詢節點1的父節點:parent[1] = -1,
查詢節點3的父節點:parent[3] = -1,
情況與第一步類似,這裡假設3為1的父節點:
圖3
更新根節點陣列,則:

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的父節點:

圖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;
	}
}