并查集(随学习进度更新)

本文介绍了并查集的基本概念,包括其如何通过合并操作划分等价类,提供了ElemNode和Equivalence模板类的详细定义,展示了构造函数、查找、合并及复制操作的代码实现。重点讲解了如何使用并查集解决实际问题,如元素查找和集合合并。
摘要由CSDN通过智能技术生成

概念

把n个不同的元素的集合划分为若干个等价类时,先把每一个对象看作是一个单元素集合,然后依次将属于同一等价类的元素所在的集合合并。在此过程中将反复地使用一个查找运算,确定一个元素在哪一个集合中。能够完成这种功能的集合就是并查集,它支持以下三种操作:

  1. Ufsets(n):构造函数,将并查集中n个元素初始化为n个只有一个单元素的子集合。
  2. Union(S1,S2):把集合S2并入集合S1中。要求S1与S2互不相交,否则没有结果。
  3. Find(d):查找单元素d所在的集合,并返回该集合的名字。

对于并查集来说,每个子集合(等价类)用一棵树表示,子集合中每个元素用树中的一个结点表示。为了处理简单,用树的根结点来代表相应的等价类集合。
在这里插入图片描述
在此,等价类树用双亲表示法表示。此外,树的根结点的双亲域设为-k,k是该树的结点数:
在这里插入图片描述

并查集的合并

在这里插入图片描述
(a)表示将S2合并到S1中的操作
(b)表示将S1合并到S2中的操作

简单的函数实现

并查集中元素结点的定义

// 并查集元素结点类
template <class ElemType>
struct ElemNode 
{
// 数据成员:
	ElemType data;				// 数据域
	int parent;	                // 双亲域 
};

并查集类模板的定义

// 并查集
template <class ElemType>
class Equivalence
{
protected:
// 并查集的数据成员:
	ElemNode<ElemType> *set;					// 存储结点的双亲
	int size;									// 结点个数

// 辅助函数
    int Find(ElemType e) const;					// 查找元素e所在等价类的根

public:
// 抽象数据类型方法声明及重载编译系统默认方法声明:
	Equivalence(ElemType es[], int sz);					// 构造sz个单结点树(等价类)
	virtual ~Equivalence();				// 析构函数
	ElemType GetElem(int p)const;
	int GetOrder(ElemType e)const;
	void Union(ElemType a, ElemType b);					// 合并a与b所在的等价类
	bool Differ(ElemType a, ElemType b);					// 如果a与b不在同一棵树上,返回true,否则返回false
	Equivalence(const Equivalence &copy);				// 复制构造函数
	Equivalence &operator =(const Equivalence &copy);	// 赋值运算符
};

并查集类模板部分函数的实现

构造函数

template <class ElemType>
Equivalence<ElemType>::Equivalence(ElemType es[], int sz)
// 操作结果:构造sz个单结点树(等价类)
{
	size = sz;									// 容量
	set = new ElemNode<ElemType>[size];						// 分配空间
	for (int i = 0; i < size; i++) {
        set[i].data = es[i];
		set[i].parent = -1;							// 每个结点构成单结点树形成的等价类
    }
}

查找结点p所在数的根

template <class ElemType>
int Equivalence<ElemType>::Find(ElemType e) const
// 操作结果:查找结点p所在树的根
{
    int p = 0;
    while (p < size && set[p].data != e)
        p++;
	if (p == size)
		throw Error("元素不存在!");					// 抛出异常
	while (set[p].parent > -1) p = set[p].parent;// 查找根
	return p;									// 返回根
}

合并A与B所在的等价类

template <class ElemType>
void Equivalence<ElemType>::Union(ElemType a, ElemType b)
// 操作结果:合并a与b所在的等价类
{
	int root1 = Find(a);						// 查找a所在树(等价类)的根		
	int root2 = Find(b);						// 查找b所在树(等价类)的根		
	if (root1 != root2) {
       set[root1].parent += set[root2].parent;//树的根结点的双亲域该树的结点数
       set[root2].parent = root1;	// 合并树(等价类)
    }
}

拷贝构造函数及重载赋值运算符

template <class ElemType>
Equivalence<ElemType>::Equivalence(const Equivalence &copy)
// 操作结果:由copy构造新对象——复制构造函数
{
	size = copy.size;							// 容量
	set = new ElemNode<ElemType>[size];						// 分配空间
	for (int i = 0; i < size; i++) {
		set[i].parent = copy.set[i].parent;				// 复制parent的每个元素
		set[i].data = copy.set[i].data;				// 复制parent的每个元素
    }
}

template <class ElemType>
Equivalence<ElemType> &Equivalence<ElemType>::operator=(const Equivalence<ElemType> &copy)
// 操作结果:将copy赋值给当前对象——赋值运算符
{
	if (&copy != this)	{
		size = copy.size;						// 容量
		delete []set;						// 释放空间
	    set = new ElemNode<ElemType>[size];						// 分配空间
	    for (int i = 0; i < size; i++) {
		    set[i].parent = copy.set[i].parent;				// 复制parent的每个元素
		    set[i].data = copy.set[i].data;				// 复制parent的每个元素
         }
	}
	return *this;
}

输出该并查集里的所有等价类

bool out[n];				// 已输出的结点值为true,否则值为false

for (i = 0; i < n; i++)
	out[i] = false;
int p = 0;				// 当前结点
while (p < n)		{	// 对没有输出的当前结点,输出其等价类
	cout << "{" << e.GetElem(p);
	out[p] = true;
	for (i = p + 1; i < n; i++)	{	// 输出等价类
		if (!e.Differ(e.GetElem(p), e.GetElem(i)))	{	// p与i在同一个等价类中
			cout << "," << e.GetElem(i);
			out[i] = true;
		}
	}
	cout << "}" << endl;
	while (p < n && out[p]) p++;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值