详解并查集

并查集

1.简介

并查集(Union-Find),是一种用于处理动态连通性问题的数据结构。最早由 Bernard A. Galler 和 Michael J. Fischer 在1964年提出。它可以高效地管理一些不相交的集合,支持合并两个集合(union)和查找某个元素所属集合(find)这两种操作。然而,并查集的概念在计算机科学领域中的应用可以追溯到更早的年代。这个数据结构主要用于处理集合的不相交问题,比如在图论算法中的最小生成树算法(如Kruskal和Prim算法)以及在图的连通性问题中经常会用到并查集。

并查集最开始被广泛应用于算法和数据结构领域,特别是在解决图论中的连通性和最小生成树等问题时。随着计算机科学的发展,特别是在并行计算、网络连接和数据库系统等领域,并查集的应用也逐渐增多。今天,并查集已经成为解决许多实际问题的重要工具,包括社交网络分析、图像处理、游戏开发、网络通信等领域。在实际工程和科研中,我们可以经常看到并查集的身影。

并查集的两个操作

  • Find:确定元素属于哪一个子集。它可以被用来确定两个元素是否属于同一子集。
  • Union:将两个子集合并成同一个集合

并查集的实现通常使用两种优化技巧:

  • 路径压缩(Path Compression): 优化find操作,使得树的深度尽量变小,以加速后续的查询。
  • 按秩合并(Union by Rank)或按大小合并(Union by Size): 优化union操作,确保合并时较小的树挂在较大的树上,从而避免树变得过高。

2.基础算法

在并查集中,每个集合由一棵树来表示,其中树的每个节点指向其父节点,直到根节点。根节点表示集合的代表元素。

初始化时,每个元素各自构成一个单元素集合,每个元素的父节点指向自己,即构成一棵只有根节点的树。

查找操作通过递归或迭代地沿着父节点指针向上查找,直到找到根节点,从而确定元素所在的集合。

合并操作将两个集合合并为一个集合,通常是将其中一个集合的根节点的父节点指向另一个集合的根节点,从而实现两个集合的合并。

#include <bits/stdc++.h>
using namespace std;

const int N=1e6;
int fa[N],h[N];//第一个存储父亲节点,第二个存储树的高度
int getroot(int x){
	if (fa[x]==x){//如果我的父亲是我自己
		return x;//那么我就是祖先
	}else {
		return getroot(fa[x]);//否则就用递归的方式去找祖先
	}
}

void merge (int x,int y){//为x向y合并
	int p=getroot(x),q=getroot(y);//先获取两个要融合的集合的祖先
	fa[p]=q;//将x的祖先的父亲设为y的祖先
	return;
}

int main() {
	for (int i=1;i<=1000;i++){//初始化
		fa[i]=i;//每个人的祖先都应该是自己
		h[i]=1;//高度为一
	}
	return 0;
}

3.路径压缩

在进行递归的时候存储一下祖先。

#include <bits/stdc++.h>
using namespace std;

const int N=1e6;
int fa[N],h[N];//第一个存储父亲节点,第二个存储树的高度
int getroot(int x){
	if (fa[x]==x){//如果我的父亲是我自己
		return x;//那么我就是祖先
	}else {
        fa[x]=getroot(fa[x]);
		return fa[x];//否则就用递归的方式去找祖先
	}
}

void merge (int x,int y){//为x向y合并
	int p=getroot(x),q=getroot(y);//先获取两个要融合的集合的祖先
	fa[p]=q;//将x的祖先的父亲设为y的祖先
	return;
}

int main() {
	for (int i=1;i<=1000;i++){//初始化
		fa[i]=i;//每个人的祖先都应该是自己
		h[i]=1;//高度为一
	}
	return 0;
}

4.按秩合并

4.1按深度合并

因为我们不知道那棵树的高度更高,所以我们可以对树的高度进行一个维护

//按深度合并
void Union(int x, int y)
{
	int fx = fa[x], fy = fa[y];
	if (fx == fy)
		return;
	if (dep[fx] > dep[fy])
		swap(fx, fy);
	fa[fx] = fy;
	if (dep[fx] == dep[fy])//注意:只有两个深度相同时,合并总深度才会增大
		dep[fy]++;
}

4.2按长度合并

和上面差不多,就不放代码了

5.例子

假设有5个元素的并查集,初始时各元素自成一个集合:

parent: [0, 1, 2, 3, 4]

进行如下操作:

  • union(0, 1)合并 0 和 1:

    parent: [0, 0, 2, 3, 4]
    
  • union(1, 2) 合并 1 和 2:

    parent: [0, 0, 0, 3, 4]
    
  • find(2)查找 2 的根:

    结果为 0
    

此时所有连通的节点都是以0为根,find(2)的结果为0,路径压缩将所有访问路径上的节点直接连接到根节点0

总结

并查集是一种简单但非常高效的数据结构,特别适用于动态连通性问题。通过路径压缩和按秩合并这两种优化技巧,并查集能够以接近常数时间处理合并和查找操作,这在图论算法中具有广泛的应用。

  • 6
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值