并查集的实现

之前没有接触过集合这种数据结构,而且还因为课程进度慢的原因就没讲,但是最近在学最小生成树的算法的时候用到了并查集这个结构。但是没学,那天和老师聊的时候,受命写了一个并查集的数据结构供大家使用。我写之前只是看了看百度的对并查集的描述和Kruskal算法的需求,就开始动手写了,因为这个学期因为数据结构喜欢上了GP编程,并通过不断的学习结构定义与算法实现,自己为了锻炼一下子,就采用了模板,而且这个并查集更多的不是仅仅提供并与查的操作,而是提供更多重载的接口,方便在不同的场合下使用,为了可扩展与可复用等,某些服务定义成了私有的供内部使用的。后来看完了普遍的并查集实现才知道自己写的复杂了好多,但是最开始写这个并查集的目的是为了锻炼自己的分析问题,解决问题的能力的。从问题的分析,到选取存储结构,再到各种操作。刚开始考虑的不够周到,发现有些方法有冲突,或是有重复等问题,最后推到重新定义方法。

我是根据下面的这个具体问题进行分析的:

如何把班上的人按照室友关系分成不同的组,最开始班里的人有假定83人,假定一共住了21个寝室,最后的效果是把这么多的人分成了21组,每一组都是一个寝室的。还有考虑到可以把这个分组扩展到更大的范围内。这里涉及到一个关系,那就是室友关系,室友关系是集合的连接桥梁。

这里用到一个结点类是描述关系的:

#pragma once
#include<iostream>
#include<fstream>
using namespace std;

template<class T> //为了支持不同的数据类型集合,采用泛型编程
class RelationNode
{
public:
	T elem; //集合元素的值
	RelationNode<T> *link; //指向同一集合的下一个元素的指针
public:
	RelationNode()
	{
		link = NULL;
	}
	RelationNode(T el)
	{
		elem = el;
		link = NULL;
	}
	~RelationNode()
	{

	}
};


还有一个是用来表示一个子集标志元素的类:

#pragma once
#include<iostream>
#include<fstream>
#include"RelationNode.h"
using namespace std;

template<class T>
class ElemNode
{
public:
	T elemFlag; //代表某一个集合的标志元素值
	RelationNode<T> *rear; //指向该集合的最后一个元素的指针 在这里为了方便合并,做成了类似与基于链式存储的队列
	RelationNode<T> *first; //指向该集合的第一个元素的指针
public:
	ElemNode()
	{
		rear = NULL;
		first = NULL;
	}
	ElemNode(T flem)
	{
		elemFlag = flem;
		rear = NULL;
		first = NULL;
	}
	~ElemNode()
	{

	}
};

整个结构框架类似于基于邻接表存储的图,下面是并查集的类:

#pragma once
#include<iostream>
#include<fstream>
#include"ElemNode.h"
#include"RelationNode.h"
using namespace std;
const int DefaultSize_UFSet = 30;

template<class T>
class UFSet
{
protected:
	ElemNode<T> *elemNodeTable; //集合元素的列表
	int maxSubSetSize; //最大的集合元素数目
	int cunrrentSubSetSize; //集合中当前的元素个数
	const T elemNull; //表示集合中不存在的元素标志
	static const int s_NullIdx = -1; //空索引标示

	int GetSubSetIndex(const T &flagelem); //根据子集标志元素获取子集的索引
	int FindSubSetIndex(const T &elem); //根据普通元素查找元素所在的子集索引
	bool IsExist(const T &elem); //判断一个元素是否存在这个并查集中
public:
	UFSet(const T &elemnull, int size = DefaultSize_UFSet); //配置元素空标志,以及集合大小(这里的大小是指子集数目)
	UFSet(T elemarray[], int arrnum, const T &elemnull, int size = DefaultSize_UFSet); //使用元素数组初始化各子集
	~UFSet();
	void MakeEmpty(); //销毁一个集合
	
	T GetSubSetFlagElem(const T &elem); //根据普通元素查找所在子集的标志元素
	void UnionSubSet(const T &flagelemOne,const T &flagelemOther); //根据两个子集合标志元素合并这两个子集合
	bool UnionSubSetCommon(const T &elemOne,const T &elemOther); //根据普通的两个元素合并他们所在的子集
	
	bool IsTheSameSubSet(const T &elemOne, const T &elemOther); //查找两个普通元素知否在同一个集合
	bool InsertOneSubSet(const T &elem); //往集合中增加一个子集(前提集合中原先没有这个子集)
	bool RemoveOneSubSet(const T &flagelem); //从集合中删除一个子集(前提存在)
	bool RemoveOneElem(const T &elem); //从并查集中删除一个元素
	
	void ResetTheSet(); //重置该集合
	void ResetTheSet(T elemarray[], const T &elemnull, int size = DefaultSize); //重置该集合,类似于构造函数
};

template<class T>
int UFSet<T>::GetSubSetIndex(const T &flagelem)
{
	for(int idx = 0; idx < cunrrentSubSetSize; idx++)
	{
		if(elemNodeTable[idx].elemFlag == flagelem)
		{
			return idx;
		}
	}

	return s_NullIdx;
}

template<class T>
int UFSet<T>::FindSubSetIndex(const T &elem)
{
	RelationNode<T> *temp_link = NULL;
	for(int idx = 0; idx < cunrrentSubSetSize; idx++)
	{
		temp_link = elemNodeTable[idx].first;
		while(temp_link != NULL)
		{
			if(temp_link->elem == elem)
			{
				return idx;
			}
			temp_link = temp_link->link;
		}
	}

	return s_NullIdx;
}

template<class T>
bool UFSet<T>::IsExist(const T &elem)
{
	if(FindSubSetIndex(elem) == s_NullIdx)
	{
		return false;
	}
	else
	{
		return true;
	}
}

template<class T>
UFSet<T>::UFSet(const T &elemnull, int size):elemNull(elemnull)
{
	maxSubSetSize = size;
	cunrrentSubSetSize = 0;
	elemNodeTable = new ElemNode<T>[maxSubSetSize];
	if(elemNodeTable == NULL)
	{
		cerr<<"内存分配错误"<<endl;
		exit(1);
	}
	for(int idx = 0; idx < maxSubSetSize; idx++)
	{
		elemNodeTable[idx].elemFlag = elemNull;
	}
}

template<class T>
UFSet<T>::UFSet(T elemarray[],int arrnum, const T &elemnull, int size):elemNull(elemnull) //根据一个外来数据数组来初始化并查集
{
	maxSubSetSize = size;
	if(maxSubSetSize < arrnum)
	{
		cerr<<"集合默认空间不够,拒绝初始化,请选择设置更大的集合默认空间"<<endl;
		exit(1);
	}

	elemNodeTable = new ElemNode<T>[maxSubSetSize];
	if(elemNodeTable == NULL)
	{
		cerr<<"内存分配错误"<<endl;
		exit(1);
	}

	for(int idx = 0; idx < arrnum; idx++) //初始化各个子集
	{
		elemNodeTable[idx].elemFlag = elemarray[idx];
		elemNodeTable[idx].first = new RelationNode<T>(elemarray[idx]); //将第一个进来的元素置为子集的标志元素
		elemNodeTable[idx].rear = elemNodeTable[idx].first; //子集中只有一个元素的时候,首尾指针指向的是同一个元素
	}

	cunrrentSubSetSize = arrnum; //当前子集数目
}

template<class T>
UFSet<T>::~UFSet()
{
	MakeEmpty(); //因为是链表存储,所以要释放空间
}

template<class T>
void UFSet<T>::MakeEmpty()
{
	RelationNode<T> *del = NULL;
	for(int idx = 0; idx < cunrrentSubSetSize; idx++)
	{
		while(elemNodeTable[idx].first != NULL)
		{
			del = elemNodeTable[idx].first;
			elemNodeTable[idx].first = del->link;
			delete del;
		}
	}
}

template<class T>
T UFSet<T>::GetSubSetFlagElem(const T &elem)
{
	int idx = GetSubSetIndex(elem);
	if(idx != s_NullIdx)
	{
		return elemNodeTable[idx].elemFlag;
	}
	else
	{
		return elemNull;
	}
}

template<class T>
void UFSet<T>::UnionSubSet(const T &flagelemOne,const T &flagelemOther)
{
	int oneIdx = GetSubSetIndex(flagelemOne); //根据子集标志获取子集索引
	int otherIdx = GetSubSetIndex(flagelemOther);
	if((oneIdx != s_NullIdx) && (otherIdx != s_NullIdx)) //这两个子集都存在
	{
		elemNodeTable[oneIdx].rear->link = elemNodeTable[otherIdx].first; //把Other子集的所有元素放到One子集中去
		elemNodeTable[oneIdx].rear = elemNodeTable[otherIdx].rear; //把尾指针处理下

		elemNodeTable[otherIdx].flagElem = elemNodeTable[cunrrentSubSetSize-1].flagElem; //把最后一个自己的信息放到被删除的子集中去
		elemNodeTable[otherIdx].first = elemNodeTable[cunrrentSubSetSize-1].first;
		elemNodeTable[otherIdx].rear = elemNodeTable[cunrrentSubSetSize-1].rear;

		elemNodeTable[cunrrentSubSetSize-1].flagElem = elemNull; //把最后一个子集销毁掉
		elemNodeTable[cunrrentSubSetSize-1].first = NULL;
		elemNodeTable[cunrrentSubSetSize-1].rear = NULL;

		cunrrentSubSetSize--; //当前子集数目相应的减少1
	}
}

template<class T>
bool UFSet<T>::UnionSubSetCommon(const T &elemOne,const T &elemOther) //针对两个最普通的元素进行合并,但有可能不成功
{
	int oneIdx = oneIdx = FindSubSetIndex(elemOne);
	int otherIdx = FindSubSetIndex(elemOther);
	if((oneIdx != s_NullIdx) && (otherIdx != s_NullIdx) && (oneIdx != otherIdx)) //两个元素在并查集中并且他们不在一个子集中
	{
		elemNodeTable[oneIdx].rear->link = elemNodeTable[otherIdx].first;
		elemNodeTable[oneIdx].rear = elemNodeTable[otherIdx].rear;

		elemNodeTable[otherIdx].elemFlag = elemNodeTable[cunrrentSubSetSize-1].elemFlag;
		elemNodeTable[otherIdx].first = elemNodeTable[cunrrentSubSetSize-1].first;
		elemNodeTable[otherIdx].rear = elemNodeTable[cunrrentSubSetSize-1].rear;

		elemNodeTable[cunrrentSubSetSize-1].elemFlag = elemNull;
		elemNodeTable[cunrrentSubSetSize-1].first = NULL;
		elemNodeTable[cunrrentSubSetSize-1].rear = NULL;

		cunrrentSubSetSize--;

		return true;
	}
	else
	{
		return false;
	}
}

template<class T>
bool UFSet<T>::IsTheSameSubSet(const T &elemOne, const T &elemOther)
{
	int oneIdx = FindSubSetIndex(elemOne);
	int otherIdx = FindSubSetIndex(elemOther);
	if((oneIdx != s_NullIdx) && (otherIdx != s_NullIdx) && (oneIdx == otherIdx))
	{
		return true;
	}
	else
	{
		return false;
	}
}

template<class T>
bool UFSet<T>::InsertOneSubSet(const T &elem)
{
	if(cunrrentSubSetSize == maxSubSetSize)
	{
		cerr<<"并查集已满,无法插入"<<endl;
		return false;
	}

	if(IsExist(elem) == true)
	{
		return false
	}
	//类似于初始化操作
	elemNodeTable[cunrrentSubSetSize-1].elemFlag = elem;
	elemNodeTable[cunrrentSubSetSize-1].first = new RelationNode<T>(elem);
	elemNodeTable[cunrrentSubSetSize-1].rear = elemNodeTable[cunrrentSubSetSize-1].first;

	cunrrentSubSetSize++;
	return true;
}

template<class T>
bool UFSet<T>::RemoveOneSubSet(const T &flagelem)
{
	int flagIdx = GetSubSetIndex(flagelem);
	if((flagIdx != s_NullIdx) && (GetSubSetFlagElem(flagelem) == flagelem))
	{
		RelationNode<T> *temp_first = elemNodeTable[flagIdx].first;
		RelationNode<T> *del = temp_first;
		while(temp_first != NULL) //删除一个子集,类似于删除链表
		{
			del = temp_first;
			temp_first = del->link;
			delete del;
		}
		//最后后面的补上
		elemNodeTable[flagIdx].elemFlag = elemNodeTable[cunrrentSubSetSize-1].elemFlag;
		elemNodeTable[flagIdx].first = elemNodeTable[cunrrentSubSetSize-1].first;
		elemNodeTable[flagIdx].rear = elemNodeTable[cunrrentSubSetSize-1].rear;

		elemNodeTable[cunrrentSubSetSize-1].elemFlag = elemNull;
		elemNodeTable[cunrrentSubSetSize-1].first = NULL;
		elemNodeTable[cunrrentSubSetSize-1].rear = NULL;

		return true;
	}
	else
	{
		return false;
	}
}

template<class T>
bool UFSet<T>::RemoveOneElem(const T &targetElem)
{
	int targetIdx = FindSubSetIndex(targetElem);
	if(targetIdx != s_NullIdx) //待删除的元素在并查集中
	{
		RelationNode<T> *temp_first = elemNodeTable[targetIdx].first;
		if(GetSubSetFlagElem(targetElem) == targetElem) //要删除的是子集标志元素
		{
			if(temp_first->link == NULL) //元素所在子集只有一个元素 删除元素相当于删除子集,不允许删除此元素
			{
				return false;
			}
			else
			{
				elemNodeTable[targetIdx].first = temp_first->link;
				elemNodeTable[targetIdx].elemFlag = temp_first->link->elem; //让子集标志的后一个元素充当该子集的标志
				
				delete temp_first;
				return true; //删除成功
			}
		}
		else //非子集标志元素
		{
			RelationNode<T> *pre_link = NULL;
			while((temp_first != NULL) && temp_first->elem != targetElem)
			{
				pre_link = temp_first;
				temp_first = temp_first->link;
			}
			if(temp_first != NULL) //成功跳转到待删除的元素处
			{
				if(temp_first->link == NULL) //待删除的是该子集的最有一个元素
				{
					elemNodeTable[targetIdx].rear = pre_link;
					delete temp_first; //删除成功
					return true;
				}
				else //待删除的元素不是子集标志也不是子集的最后一个元素,是最普通的子集中间的元素
				{
					pre_link->link = temp_first->link;
					delete temp_first;
					return true;
				}
			}
			else //未能成功跳转到待删除元素
			{
				return false;
			}
		}
	}
	else
	{
		return false;
	}
}

看完别人写的并查集才知道自己是有多水了。


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值