并查集——在线等价类与离线等价类

本文介绍了一种名为并查集的数据结构及其优化方法。并查集主要用于解决离线或在线等价类问题,如合并两个类为一个类的操作及判断两个类是否属于同一类的问题。文章详细介绍了基本的并查集实现,并提出了三种优化策略:重量规则、高度规则和路径压缩,以提高查找效率。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

在线等价类:

解决问题

  1. 合并两个类为一个类:

    通过union操作实现;

  2. 判断两个类是否在同一个类中:

    借用find操作找到节点的根,再进行判断

首先我们给节点编号1~n作为索引一边处理

基本c++实现

class BCJ
{
private:
	int n; 
	int *parent;	
	//n 是节点总数;建立一维数组parent,记录某节点的父亲节点(规定根节点的父亲是 0 ) 
public:
	//初始化每个节点的parent为0(表示每个节点初始都是一个集合) 
	BCJ()
	{
		int *parent = new int[n+1];
		for(int i=1;i<=n;i++)
		parent[i]=0;
	}
	//Union(i,j)操作,是把根为i, j 的两棵树进行合并 
	void Union(int i, int j)
 	{
 		parent[j] = i; 
 		//这里是把根为 j 的子树合并到根为 i 的子树上合并后的根为 i 
 	}
 	//Find(i)操作,返回节点 i 的 根节点 
 	int Find(int i)
	{	//因为parent里存的是每个节点的父亲节点,所以不断向上回溯,
		//直到找到根节点<其父亲为0> ) 
 		while(parent[i] != 0)
			i=parent[i];
		return i;
	}	
} ;

优化

分析Find(i)操作,如果树很高的话,其操作效率下降许多,访问次数是从根到节点i的高度;
so,我们可以使用下述方法对其进行优化:

  1. 重量规则:在Union操作中,将两棵子树中节点较多的子树的根作为新树的根节点
  2. 高度规则:在Union操作授内阁,将两棵子树中高度较高的子树作为新树的根节点

路径压缩:缩短元素 i 到达根节点的路径。
方法有三种:

  1. 紧凑路径发、
  2. 路径分割法、
  3. 路径对折。

下面是C++实现

class betterBCJ
{
private:
	int n; 
	int *parent, *root;	
	//n 是节点总数;建立一维数组parent,记录某节点的父亲节点(规定根节点的父亲是 0 ) 
public:
	//初始化每个节点的parent为0(表示每个节点初始都是一个集合) 
	betterBCJ()
	{
		parent = new int[n+1];
		root = new int[n+1];
		for(int i=1;i<=n;i++)
		{
			parent[i]=1;
			root[i]=true;
		}
	}
	//Union(i,j)操作,是把根为i, j 的两棵树进行合并 
	void Union(int i, int j)
 	{	//重量规则 
 		if(parent[i] < parent[j])
 		{
 			parent[j] += parent[i];
			root[i]=false;	
			parent[i]=j;
		}//把根为 i 的子树合并到根为 j 的子树上
		else
		{
			parent[i]+=parent[j];
			root[j]=false;
			parent[j]=i;
		}//把根为 j 的子树合并到根为 i 的子树上
 		//这里是把根为 j 的子树合并到根为 i 的子树上合并后的根为 i 
 	}
 	//Find(i)操作,返回节点 i 的 根节点 
 	int Find(int i)
	{	//因为parent里存的是每个节点的父亲节点,所以不断向上回溯,
		//直到找到根节点<其父亲为0> ) 
 		int j=i;
		while( !root[j])
			j=parent[j];
		int f=i;
		while(i != j)
		{
			f=parent[i];
			parent[i]=j;
			i=f;
		  }  
		return j;
	}	
} ;
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值