B站燈籠哥(海外名校博士)並查集筆記
import org.junit.Test;
public class Math {
//並查集,判斷無向連通圖中是否有環(通過點合併在集合當中)
// 克魯斯卡爾算法便是使用的並查集方式
//算法步驟分析:
//1.先準備好parent數組,初始化爲-1,表示其沒有父節點處於最頂層
//2.構建集合將有聯繫的數放在一個集合中,那麼這個過程是通過合併得到的
//3.判斷圖中是否有環,根據每一條邊使用find_root來找到,如果結果相同那麼說明有環:
//原因:結果相同代表我們的這兩個點是有一條路徑可以到達的,而你在輸入這兩個點時又是一條邊,故成環
//注意:在一個集合裏表示元素之間是可以相互到達的
int find_root(int x,int[] parent)
{
while(parent[x]!=-1)
x=parent[x];
return x;
}
boolean union_verticles(int x,int y,int[] parent)
{
int x_root = find_root(x,parent);
int y_root = find_root(y,parent);
if(x_root==y_root)
{
return false;//成環,合併失敗
}
else {
parent[x_root]=y_root;
return true;
}
}
void initialize(int[] parent){
for(int i=0;i<parent.length;i++)
parent[i]=-1;
}
@Test
public void Test()
{
int edges[][] = {{1,2},{3,4},{2,3}};
int parent[] = new int[5];
initialize(parent);
boolean flag=true;
for(int i=0;i<edges.length;i++)
{
int x = edges[i][0];
int y = edges[i][1];
if(!union_verticles(x,y,parent)){
System.out.println("Circle detected");
flag =false;
break;
}
}
if(flag)
{
System.out.println("None!");
}
}
}
上述代碼還存在不足,就是可能會造成構建的集合,這樣在查找根節點的過程中會十分耗時,因此我們要進行壓縮.使用rank數組來記錄每個節點的層數(就是指包括它本身在內往下一共最大多少層),初始化爲0
修改代碼如下:
import org.junit.Test;
public class Math {
//只修改了union_verticles函數
int find_root(int x,int[] parent)
{
while(parent[x]!=-1)
x=parent[x];
return x;
}
boolean union_verticles(int x,int y,int[] parent,int rank[])
{
int x_root = find_root(x,parent);
int y_root = find_root(y,parent);
if(x_root==y_root)
{
return false;//成環,合併失敗
}
else
{
if(rank[x_root]>rank[y_root])//說明y_root層數更高一點,連接後兩個rank均不變
{
parent[y_root]=x_root;
}
else if(rank[x_root]<rank[y_root])//說明x_root層數更高一點,連接後兩個rank均不變
{
parent[x_root]=y_root;
}else
{
parent[x_root]=y_root;//由於層數相同,連接後rank[y_root]會變大,加一層
rank[y_root]++;//其實這一部分也可以寫爲parent[y_root]=x_root;rank[x_root]++
}
return true;
}
}
void initialize(int[] parent){
for(int i=0;i<parent.length;i++)
parent[i]=-1;
}
@Test
public void Test()
{
int edges[][] = {{1,2},{3,4},{2,3},{4,2}};
int parent[] = new int[5];
int rank[] = new int[5];
initialize(parent);
boolean flag=true;
for(int i=0;i<edges.length;i++)
{
int x = edges[i][0];
int y = edges[i][1];
if(!union_verticles(x,y,parent,rank)){
System.out.println("Circle detected");
flag =false;
break;
}
}
if(flag)
{
System.out.println("None!");
}
}
}