并查集

1. 并查集是一种维护集合的数据结构,支持下面两个操作:

① 合并:合并两个集合

② 查找:判断两个元素是否在一个集合中

2. 并查集使用什么实现的呢?其实就是使用的是一个数组

并查集的基本操作:总的来说并查集的使用先需要先初始化father数组,然后再根据需要进行查找或者合并的操作

① 初始化

一开始每一个元素都是一个独立的集合,因此需要令所有father[i]等于i

father[i]表示元素i的父节点,例如father[2] = 1表示元素2的父亲节点是1,而父节点也是属于这个集合内的元

for(int i = 1; i <= N; i++){
    father[i] = i;
}

② 查找

由于规定同一个集合中只存在一个根节点,因此查找操作就是对给定的节点寻找其根节点的过程,实现的方式可以是递归或者递推,但是其思路都是一样的,即反复寻找父节点,找到找到根节点

 下面是递推的代码:

//findFather函数返回元素x所在集合的根节点
int findFather(int x){
    while(x != father[x]){
        x = father[x];
    }
    return x;
}

 递归代码:

int findFather(int x){
    if(x == father[x]) return x;
    return findFather(father[x]);
}

3. 合并

合并是指把两个集合合并成一个集合,题目中一般给出两个元素,要求把这两个元素所在的集合合并,具体实现上一般是先判断两个元素是否属于同一个集合,只有当两个元素属于不同集合的时候才合并,而合并的过程一般是把其中一个集合的根节点的父亲指向另外一个集合的根节点,所以主要分为以下两步:

① 对于给定的两个元素a,b判断他们是否属于同一集合,可以调用上面的查找函数,对这两个元素a,b分别查找根节点,然后判断根节点是否相同

② 合并两个集合:在①中已经获得了两个元素的根节点faA和faB,因为只需要把其中一个节点的父节点指向另外一个节点,例如可以将father[A] = faB当然反过来也是可以的,father[B] = A两者没有什么区别

下面是将元素4和元素6进行合并:

① 判断4和6是否属于同一个集合,元素4所在的根节点是1,元素6所在的根节点是5因此他们不属于同一个集合

② 合并两个集合:令father[5] = 1,即把元素5的父节点设为1,于是有了合并后的集合,如图所示:

 

 

合并的代码:

void Unin(int a, int b){
    int faA = findFather(a);
    int faB = findFather(b);
    if(faA != faB){
        father[faA] = faB;
    }
}

并查集的一个性质:因为在合并的过程中,只对两个不同的集合进行合并,如果有两个元素在相同的集合中,那么就不会对他进行操作,这就保证了一个集合中一定不会产生环,也就是并查集的每一个集合都是一棵树

4. 虽然说上面的代码已经可以完成查找与合并的操作,但是查找函数是没有进行优化的,在极端情况下的效率会比较低,现在来考虑一种情况就是给定很多元素并且形成了一条链,那么这个查找函数效率会非常低,而findFather函数的目的就是为了要查找根节点,我们可以对其进行下面的优化:

在查找的时候将当前节点到根节点的路径上的节点的父节点都指向根节点,查找的时候就不用一直回溯去找父节点了,查询的复杂度可以降低为O(1)

如何实现转换呢?回忆之前的查找函数可以知道是从给定节点不断获得其父节点而最终到达根节点的,所以可以分为两个步骤:

① 按原先的写法获得x的根节点r

② 重新走一遍x寻找根节点的路径,把路径上所有节点的父节点全部改为这个集合中的根节点,可以写出如下的代码:

 

递推的代码如下:

    //递推的的代码
	static int findFather(int x){
		//由于x在下面的while中会变成根节点因此把原先的节点先保存一下
		int a = x;
		while(x != father[x]){
			x = father[x];
		}
		while(a != father[a]){
			int z = a;
			a = father[a];
			father[z] = x;
		}
		return x;
	}

递归的代码如下:

static int findFather(int v){
	if(v == father[v]) return v;
	int F = findFather(father[v]);
	father[v] = F;
	return F;
}

递归的时候找到了根节点那么层层进行返回,而每一次都是返回的是根节点而对应的v是不一样的,为v到根节点路径上节点的节点,层层返回的时候将这些路径上的节点的父节点都指向了根节点

 

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值