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