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!");
}
}
}