并查集

并查集

并查集基础

定义

并查集是一种树型的数据结构,主要用于处理一些不相交集合的合并及查询问题(即所谓的并、查)。一些常见的用途有连通子图、最小生成树的Kruskal算法和求最近公共祖先等。

并查集的实现与优化

并查集的原理:

  • 每个集合用一棵树来表示。树根的编号就是整个集合的编号。每个节点存储它的父节点,pre[x]表示x的父节点。
  • 如果x为树根的话,pre[x]=x (x的父节点指向自己)
  • 求x的集合编号(x的祖宗节点):while(pre!= x ) x = pre[x];
  • 合并两个集合,将x的根节点嫁接到y的根节点,如:prex是x的集合编号,prey是y的集合编号,嫁接:pre[prex]= prey;

代码实现

int f[maxn]; //全名为 father,父节点的意思

//初始化 n个元素
void Init()
{
    //使每个元素的根节点是其本身
    //即初始时每个元素都是单独的
    for(int i=1; i<=n; i++)
        f[i]=i;
}
//查询树的根
//非递归实现
int Find(int i)
{
	while(f[i] != i)   //直到元素的父节点是它本身,表示已经查询到了树的根
		i = f[i];
	return i; 			//返回根节点对应的元素
}

//递归实现
int Find(int i)
{
	if(f[i]==i)      //若元素的根节点为其本身,那么此元素就是树的根
		return f[i];    //直接返回元素本身即可
	else
		return Find(f[i]); //否则继续查询,知道查询到树的根位置
}

//简化版
int Find(int i)
{
	return f[i]==i ? f[i] : Find(f[i]);
}
//合并
void merge(int a, int b)
{
	//先找到两个元素对应的根对应的元素
	int fa = Find(a);
	int fb = Find(b);
	if(fa==fb) return;
	else f[fa]=fb;  //否则令元素 a的根指向元素 b的根
}

//简化版
void merge(int a, int b)
{
	f[Find(a)] = Find(b);
}

代码优化

优化1:按秩合并

首先,我们合并时,可记录这棵树的高度(记为rank)。接下来当我们需合并两棵树时,我们先对两棵树的高度进行判断,如不同,则让高度小的树的根指向高度大的根。

//初始化优化
int f[maxn];
int h[maxn]; //全名为 height

void Init()
{
	for(int i=1; i<=n; i++)
    {
		f[i] = i;
		h[i] = 0;  //令每棵树的高度初始值都为 0
	}
}
//合并优化
void merge(int a, int b)
{
	int fa = Find(a);
	int fb = Find(b);
	if(fa==fb) return;

	if(h[fa] < h[fb])
    {  //如果元素 a对应的树的高度比 b小
		f[fa] = fb;  //使元素 a的根指向元素 b的根
	}
    else
    {
		f[fb] = fa;  //否则让元素 b的根指向元素 a的根
		if(h[fa] == h[fb]) h[fa]++;
		// 如果两者对应的树的高度相同,则使新生成的树高度 +1
	}
}

优化2:路径压缩

由于查询时我们需沿着元素所在的树从下往上查询,最终找到这棵树的根,表明这个元素与其根对应元素属于同一组。因为在此查询过程中我们会经过许多节点,而如果我们能将这个元素直接指向根节点,那么就能节省许多查询的时间。同时,在查询过程中,每次经过的节点,我们都可以同时将他们一起直接指向根节点。这样做的话,我们再查询这些节点时,就能很快知道他们的根是谁了。

//查询优化
int Find(int i)
{
	return f[i]==i ? f[i] : f[i] = Find(f[i]);
	//使元素直接指向树的根
}

最终代码实现

void Init()
{
	for(int i=0; i<=n; i++)
    {
		f[i] = i;
		h[i] = 0;
	}
}
int Find(int i)
{
	return f[i]==i ? f[i] : f[i]=Find(f[i]);
}
void merge(int a, int b)
{
	int fa = Find(a);
	int fb = Find(b);
	if(fa != fb)
    {
		if(h[fa] < h[fb])
        {
			f[fa] = fb;
		}
        else
        {
			f[fb] = fa;
			if(h[fa] == h[fb]) h[fa]++;
		}
	}
}

并查集入门模板题

https://blog.csdn.net/weixin_51250927/article/details/113621645

并查集进阶

带权并查集

定义:

带权是指一个元素具有额外的信息。比如元素1对应着数值6,元素2对应着数值4。这些带有信息的元素组成的并查集即为带权并查集。

作用:

带权并查集能计算各个小组中元素的个数、能计算n个元素中还有几个元素没有加入小组中;计算分数、距离等等

注释:带权并查集需要在路径优化的基础下进行。

带权并查集模板题:

https://blog.csdn.net/weixin_51250927/article/details/113620822

种类并查集

定义;

种类并查集是能把并查集分为几个部分,每个部分的种类不同。

作用:

种类并查集能将两个阵容分开。比如把好人和坏人分为两个阵营。

https://blog.csdn.net/weixin_51250927/article/details/113617248

https://blog.csdn.net/weixin_51250927/article/details/113619285

posted @ 2023-02-05 00:32  PassCo2  阅读(57)  评论(0)    收藏  举报