想要弄明白什么是并查集首先要明白什么是等价类
我们说关系R是一个等价关系当且满足以下条件:
1.对于所有的a∈U,有(a,a)∈R时(关系是自反的)
2.(a,b)∈R,当且仅当(b,a)∈R(关系是对称的)
3.若(a,b)∈R且(b,c)∈R,则有(a,c)∈R(关系是传递的)
比如R={(1,11),(7,11),(2,12),(12,8),(11,12),(3,13),(4,13),(13,14),(14,9),(5,14),(6,10)}
根据等价关系和对称关系我们发现里面包含三个等价类。
{1,2,7,8,11,12},{3,4,5,9,13,14},{6,10}
在每个等价类里面知道了其中一个元素,就能根据等价关系R推出等价类里面的其他元素。
并查集主要由一个整数型的数组和两个函数构成。数组pre[]记录了每个点的前导点是什么,函数find是查找,join是合并。
以下为使用数组实现并查集算法
int pre[1000];
int find(int x) //查找根节点
{
int r=x ; //委托r去找根节点
while(pre[r]!=r) //如果r是顶节点,那么pre[r]就是它自己
r=pre[r]; //不断地向上方进行查找,知道找到根节点
return r;
}
void join(int x,int y)
{
int fx=find(x),fy=find(y);
if(fx!=fy) //如果两个等价类的根节点不同,则合并等价类。
pre[fx]=fy;
}
以上参考与该博主的博客,更详细的解答请移步这里:https://blog.youkuaiyun.com/the_best_man/article/details/62416938
以下为链表的实现:
#include<iostream>
#include<stdio.h>
#include<set>
#define N 50001
using namespace std;
set<int> s;
struct Point
{
int x;//用来存储父类点
Point *root;//用来存储链表根节点的地址
Point *next;//用来存储下一个节点的地址
Point *tail;//用来存储链表尾部节点的地址
}p[N];
int n;
void makeSet()//初始化序列集
{
for(int i=1;i<=n;i++)
{
p[i].root=&p[i];//初始化时自己的根节点就是自己
p[i].x=i;//初始时根就是自己
p[i].next=NULL;//next没有指向其他
p[i].tail=&p[i];//尾部指针就是自己
}
}
int findRoot(int x)
{
return p[x].root->x;
}
void update(Point* root,Point* p)
{
while(p!=NULL)//将P链表的根节点更新为root的根节点
{
p->root=root;
p=p->next;
}
while(root!=NULL)//将root链表的尾节点更新为p节点的尾节点。
{
root->tail=p->tail;
root=root->next;
}
root->tail->next=p;//将两个链表链接起来
root->tail=p->tail;
}
void merge(int a,int b)
{
Point* ra=p[a].root;
Point* rb=p[b].root;
if(ra!=rb)
update(rb,ra);
}
以上代码我在原博主代码基础上进行了简化,源代码链接点击这里:https://blog.youkuaiyun.com/jiakai0419/article/details/7518525