并查集

定义

并查集是一种树型的数据结构,用于处理一些不相交集合(Disjoint Sets)的合并及查询问题。
并查集初始化时是一些单独的的点。如下图。

 点之间发生了关系,于是连成了线。

一条链的祖宗成为另一条链祖宗的儿子。于是链成了树。

 其实并查集相当于一个庞大的家族,家族里的人有的是亲戚关系。当x脉中的任意一人与y脉中的任意一人是亲戚关系,则这两脉都是亲戚关系。

基本操作

1.初始化

首先定义一个fa数组,初始化就是令数组中每个人的爸爸都是他自己。所以f[1]暂时(这里很重要)表示1的爸爸。当出现这种情况时,它就是这一脉的祖宗。代码如下

void Initialize()
{
    for(int i=0;i<n;i++) parent[i]=i;
}

  

2.查找

小x想知道自己的祖宗是谁,但他只知道自己的爸爸是谁。于是他问他爸爸“”你是我祖先吗?“”他的爸爸不是他祖宗,所以他爸爸问小x的爷爷,也就是他自己的爸爸说:“”你是我祖先吗?“”小x爷爷也不是,也就继续往上问。如此循环下去(假设这一脉长生不老),直到有一个人的爸爸是是他自己,也就是说他是这一脉的祖先。而用代码有两种写法,分别是递归、循环。在这里我只个人推荐递归写法。

如果我们要询问1的祖宗(假设是5)我们不得不询问2,3,4,5.这样太慢。每次查找复杂度都是O(n),太慢了!

所以此代码还有一个路径压缩,到这一步。fa数组就不表示爸爸了。为了方便询问,我们自动把f[2],f[3],f[4]都赋成5。方便后续找祖先。

int findf(int x)
{
	if(fa[x]==x) return x; //自己的爸爸是自己,返回自己的编号
	return fa[x]=findf(fa[x]); //否则询问自己的爸爸
}

 3.合并

小x和小y成为了亲戚,于是他们这两脉的成为亲戚关系。但只能有一个祖先。怎们办呢?正当他们焦头烂额的时候,我突发奇想说道:“”可以让x祖先成为y祖先的爸爸,这样不就使你们同属一个集合了吗。“”。x脉表示同意,y祖宗虽然有些不愿意。但看在我的面子上也同意了。而这就是并查集的精髓——集合合并。

void Union(int x,int y)
{
	int fx=findf(x),fy=findf(y); //fx表示x的祖宗,fy表示y的祖宗。 
	fa[fx]=fy; //令y的祖宗成为x的祖宗,这样不管是x脉还是y脉询问祖宗时都是y的祖宗。 
}

图示

注:箭头指向自己的爸爸。

推荐

最后推荐一道模板题:洛谷P3367 【模板】并查集

 

原文地址:https://www.cnblogs.com/zxjhaha/p/11207928.html