【算法与数据结构】——并查集

参考算法与数据结构——并查集
同时加入了一些自己的理解,简化了很多。侵删。

概论

定义:
并查集是一种树型的数据结构,用于处理一些不相交集合的合并及查询问题(即所谓的并、查)。比如说,我们可以用并查集来判断一个森林中有几棵树、某个节点是否属于某棵树等。

主要构成:
并查集主要由一个整型数组pre[ ]和两个函数find( )、join( )构成。
数组 pre[ ] 记录了每个点的前驱节点是谁,函数 find(x) 用于查找指定节点 x 属于哪个集合,函数 join(x,y) 用于合并两个节点 x 和 y 。

作用:
并查集的主要作用是求连通分支数(如果一个图中所有点都存在可达关系(直接或间接相连),则此图的连通分支数为1;如果此图有两大子图各自全部可达,则此图的连通分支数为2……)

并查集的初始化

将所有元素的前驱都设为他自己。

find()函数

我们需要定义一个数组:int pre[1000]; (数组长度依题意而定)。这个数组记录了每个元素的前驱结点是谁。比如说pre[16]=6就表示16号元素的前驱结点是6号。如果一个元素的前驱结点就是他自己,那说明他就是代表元了,查找到此结束。也有一个元素自及组成一个集合的,那么这个集合的代表元就是自己。

int find(int x)					//查找x的代表元
{
	while(pre[x] != x)			//如果x的前驱结点不是自己
		x = pre[x];
	return x;
}

join函数

join函数的作用是将两个集合合并,合并两个集合的关键是找到两个集合的代表元,将其中一个作为另一个的前驱结点即可。

void join(int x,int y)                     
{
    int fx=find(x), fy=find(y); 
    if(fx != fy) 
        pre[fx]=fy;	//fy做fx的前驱结点
}

千万注意不要直接将读入的两个数据,其中一个作为另一个的前驱结点,而应该是找到两个数据的代表元,将两个代表元合并,也就是将两个集合合并。

路径压缩算法:优化find( )函数

这里并没有明确谁是谁的前驱的规则,而是我直接指定后面的数据作为前面数据的前驱。那么这样就导致了最终的树状结构无法预计,即有可能是良好的 n 叉树,也有可能是单支树结构(一字长蛇形)。试想,如果最后真的形成单支树结构,那么它的效率就会及其低下(树的深度过深,那么查询过程就必然耗时)。
而我们最理想的情况就是所有人的直接上级都是教主,这样一来整个树的结构就只有两级,此时查询教主只需要一次。因此,这就产生了路径压缩算法。
实现:
从上面的查询过程中不难看出,当从某个元素出发去寻找它的代表元时,我们会途径一系列的元素,在这些节点中,除了代表元外,其余所有节点都需要更改直接前驱为曹操。
因此,基于这样的思路,我们可以通过递归的方法来逐层修改返回时的某个节点的直接前驱(即pre[x]的值)。简单说来就是将x到根节点路径上的所有点的pre(上级)都设为根节点。下面给出具体的实现代码:

int find(int x)     				//查找结点 x的根结点 
{
    if(pre[x] == x) return x;		//递归出口:x的上级为 x本身,即 x为根结点        
    return pre[x] = find(pre[x]);	//此代码相当于先找到根结点 rootx,然后pre[x]=rootx 
}

该算法存在一个缺陷:只有当查找了某个元素的代表元后,才能对该查找路径上的各节点进行路径压缩。换言之,第一次执行查找操作的时候是实现没有压缩效果的,只有在之后才有效。

路径压缩算法:加权标记法

主要思路:
加权标记法需要将树中所有节点都增设一个权值,用以表示该节点所在树中的高度(比如用rank[x]=3表示 x 节点所在树的高度为3)。这样一来,在合并操作的时候就能通过这个权值的大小来决定谁当谁的前驱结点。

在实际写代码时,为了使代码尽可能简洁,我们可以将第1点单独作为一个逻辑选择,然后将2、3点作为另一个选择(反正第2点任意指定上级嘛),所以具体的代码如下:

void union(int x,int y)
{
    x=find(x);							//寻找 x的代表元
    x=find(y);							//寻找 y的代表元
    if(x==y) return ;					//如果 x和 y的代表元一致,说明他们共属同一集合,则不需要合并,直接返回;否则,执行下面的逻辑
    if(rank[x]>rank[y]) pre[y]=x;		//如果 x的高度大于 y,则令 y的上级为 x
    else								//否则
    {
        if(rank[x]==rank[y]) rank[y]++;	//如果 x的高度和 y的高度相同,则令 y的高度加1
        pre[x]=y;						//让 x的上级为 y
    }
}
  • 12
    点赞
  • 120
    收藏
    觉得还不错? 一键收藏
  • 8
    评论
并查集是一种常用的数据结构,用来解决集合的合并和查找问题。它具有快速的合并和查找操作,且在某些场景下比其他数据结构更为高效。 并查集包含以下几个关键步骤: 1. 初始化:首先将每个元素作为一个单独的集合,每个集合代表一个独立的元素。 2. 查找操作:通过查找操作可以快速找到某个元素所属的集合。这里使用了路径压缩的优化方法,即在查找的同时将当前元素直接指向根节点,以加速以后的查找操作。 3. 合并操作:当需要将两个元素所属的集合合并时,可以通过合并操作将一个元素的根节点指向另一个元素的根节点。这样就能实现将两个集合合并成一个集合的目的,从而实现集合的合并操作。 并查集的核心思想是通过维护每个元素的根节点,来判断元素之间的关系。如果两个元素具有相同的根节点,说明它们属于同一个集合;如果两个元素具有不同的根节点,说明它们属于不同的集合。 并查集可以解决一些实际问题,例如判断无向图中的两个节点是否连通,以及朋友圈的数量等。在这些问题中,我们可以使用并查集来维护每个节点所属的集合,从而更高效地进行相关的操作。 总之,并查集是一种有效处理集合合并和查找问题的数据结构,具有简单、高效的特点。通过合理地应用,并查集能够在解决某些实际问题时,提供更高效的算法实现。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 8
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值