之前没有接触过集合这种数据结构,而且还因为课程进度慢的原因就没讲,但是最近在学最小生成树的算法的时候用到了并查集这个结构。但是没学,那天和老师聊的时候,受命写了一个并查集的数据结构供大家使用。我写之前只是看了看百度的对并查集的描述和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;
}
}
看完别人写的并查集才知道自己是有多水了。