并查集代码介绍
并查集是一个利用无向图的性质来确定集合关系的简单算法。可以用来确定某两个元素是否在同一个集合,或者也可以用来查询是否在无向图中构成了环。查询速度很快,代码也很短,是一个简单的算法。(只是复杂度不太好确定)
确定一个集合的方法是将所有该集合中的根节点都确定为该集合中的唯一一个。当某两个元素在同一集合时,他们一定都有同一个根节点。
这里介绍用数组实现的方法。
parent数组的初始化
#define LEN 100
void f(int parent[]) //parent数组来确定元素的父亲节点在哪,同时下标代表该元素
{ //初始化数组
int i;
for(i=0;i<LEN;i++){
parent[i]=i; //当父亲节点是自己时,为根
}
}
找到该元素的根
int find(int t,int parent[])
{
while(parent[t]!=t) t=parent[t]; //父亲节点是自己的才是根
return t;
}
建立同一集合中两个元素的关系,使parent数组有指向
void merge(int a,int b,int parent[]) //a和b在同一个集合
{
int t1=find(a,parent),t2=find(b,parent); //找到a的根,找到b的根
parent[t2]=t1; //更新其中一个根使其指向另外一个根,确保根唯一
}
查询只需要对建立稍作修改就行了
void f(int a,int b,int parent[]) //查询是否在同一个集合
{
int t1=find(a,parent),t2=find(b,parent); //找到a的根,找到b的根
if(t1==t2) return 1; //根相等则在同一个集合
else return 0;
}
但是这样实现可能使并查集退化
这里有两种优化方法
路径压缩:
int find(int t,int parent[])
{
if(t!=parent[t]){
parent[t]=find(parent[t],parent);
//查询根时将每一个路径上的节点都分别都指向根,让路径变短
}
return parent[t];
}
按秩合并:
借助一个新的rank数组来记录每个根的最长路径,注意rank[跟]才有长度,将短的根指向长的根,来达到接近平衡不至于退化成链表。
void merge(int a,int b,int parent[],int rank[])
{
int t1=find(a,parent),t2=find(b,parent);
if(rank[t1]>rank[t2]){ //确保短根指向长的根
parent[t2]=t1;
}else{
parent[t1]=t2;
if(rank[t1]==rank[t2]){ //当长度相等时,随便将一个根指向另外一个根,然后长度加一
rank[t2]++;
}
}
}