-
并查集
顾名思义,就是并和查操作,对象是一个集合。集合由并操作创造,通过对集合的查来寻找连通关系。集合我们通常用pre[i]数组表示,pre[i]表示i的上一级是pre[i]的值,也就是说,他们有连通关系。
-
预知
假设a-b,b-c,c-d,d-e,e-f。-表示连通关系,为了方便,每次并操作我们将左边的对象当作右边对象的上一级,我们就有pre[b]=a,pre[c]=b,pre[d]=c,pre[e]=d,pre[f]=e。假如我们想知道a,b是否存在连通关系,我们查找pre[b],发现等于a,故a,b连通。又或者查找e,a是否连通,我们先找到pre[e]=d,不是?再找pre[d]=c,还不是?。。。直到pre[b]=a,发现可以通过不断查找上一级从而得知e,a连通。当然,若到头了,比如找。e,g是否连通,直到pre[b]=a,pre[a]=a(见下列初始化),依旧未找到g,则表明额e,g不连通。这样,大概就明白并查集是怎么一回事了。不过对于上述操作,还有优化操作,因为一级一级往上找的操作太慢了,万一数据大就不好了。
-
优化
我们知道最终e还是连通a,只不过查找步骤多,那为什么不直接把e的上一级设置为a呢?其实是可以的。同理,其他的最最最上级都是a,我们都可以将他们的上一级设置为a,这样,不管查哪两个顶点相通,只需要比较这两个顶点的上一级是否相同,就可以判断这两个点是否相通。具体操作如下的并操作。
-
集合的创建
-
初始化
刚开始,由于还未知晓任何连通关系,我们认为它与自己连通,即p[i]=i
-
查操作
查操作是为并操作做铺垫,有了查操作,我们知道a,b是否已经有了相同的上级,这样判断是否他们需要进行并操作
int find_pre(int x)
{
if(pre[x]==x)
return x;
return find_pre(pre[x]);
}
这个程序表明当pre[x]==x时就返回x,因为最上级没有上级,而初始化时是他本身
- 并操作
void unite(int x,int y)
{
int rootx=find_pre(x);//查找x的上一级
int rooty=find_pre(y);//查找y的上一级
if(rootx!=rooty)//如果x,y的上一级不相等,将x作为y的上一级
pre[rooty]=rootx;
}
- 检查是否连通
if(find_pre(source)==find_pre(destination))
return true;
return false;
最终代码如下
int pre[200005];
int find_pre(int x)
{
if(pre[x]==x)
return x;
return find_pre(pre[x]);
}
void unite(int x,int y)
{
int rootx=find_pre(x);
int rooty=find_pre(y);
if(rootx!=rooty)
pre[rooty]=rootx;
}
bool validPath(int n, int** edges, int edgesSize, int* edgesColSize, int source, int destination){
int i;
for(i=0;i<n;i++)
{
pre[i]=i;
}
for(i=0;i<edgesSize;i++)
{
unite(edges[i][0],edges[i][1]);
}
if(find_pre(source)==find_pre(destination))
return true;
return false;
}