并查集可以看成一个多棵树组成的森林,它用双亲表示数组来存储。属同一棵树的元素属于同一等价类。
加权规则:在合并(Union())时小树挂在大树上,以避免树结构的过度退化。
折叠规则:在查找(Find())后将所查元素的路径上的所有元素回序挂在树根上,以改善树的性能,减少以后查找所需的时间。
树的双亲表示数组节点类ElemNode.h
//树的双亲表示数组的节点
struct ElemNode
{
char data;//数据域
int parent;//双亲域
};
并查集森林UFSets.h
#include "ElemNode.h"
#include <iostream>
using namespace std;
//并查集森林
class UFSets
{
protected:
ElemNode *sets;//并查集树的双亲表示数组
int size;//数组长度
public:
UFSets(char es[],int n);//构造器
void Show();//显示
/*重要的函数*/
int GetOrder(char e)const;//取指定元素下标
int Find(char e);//取指定元素所在等价类根下标
void Union(char a,char b);//合并所在等价类
bool Differ(char a,char b);//判断是否在同一等价类
};
//构造器
UFSets::UFSets(char es[],int n)
{
size=n;//设置数组长度
sets=new ElemNode[size];//分配空间
for(int i=0;i<size;i++)//两个域都要初始化
{
sets[i].data=es[i];//对象的名称
sets[i].parent=-1;//每个对象开始都自己是一个等价类
}
}
//显示双亲表示数组
void UFSets::Show()
{
for(int i=0;i<size;i++)
{
cout<<"["<<sets[i].data<<","<<sets[i].parent<<"]";
}
}
//取指定元素下标
int UFSets::GetOrder(char e)const
{
int i;
for(i=0;i<size;i++)
if(sets[i].data==e)
return i;//找到返回下标
return -1;//没找到返回-1
}
//查:指定元素所在等价类根下标(带折叠规则)
int UFSets::Find(char e)
{
int p=GetOrder(e);//取e下标给p
if(p==-1)
return -1;//没有e故也找不到根
int i=p;//i用来向上找i的根
while(sets[i].parent>-1)//双亲非负时
i=sets[i].parent;//游标i向双亲走
//return i;//不做折叠时在这返回等价类根下标
//折叠规则:查找路径上的子树回序挂到根上去
if(p!=i)//为根时什么都不做
{
while(sets[p].parent!=i)//p向上走,还没连到根i时
{
int k=sets[p].parent;//记录p双亲游标
sets[p].parent=i;//p挂到i上去
p=k;//p再变为其双亲(回序走)
}
}
return i;//返回等价类根下标
}
//并:两元素所在等价类(带加权规则)
void UFSets::Union(char a,char b)
{
int r1=Find(a);//a所在等价类根下标
int r2=Find(b);//b所在等价类根下标
if(r1!=-1 && r2!=-1 && r1!=r2)//两个根都找到且不同时可做合并
{
int temp=sets[r1].parent+sets[r2].parent;//记录两根parent域之和
//加权规则:小树挂大树
if(sets[r1].parent<=sets[r2].parent)//r2为小树
{
sets[r2].parent=r1;//小树的根挂在大树根上
sets[r1].parent=temp;//大树元素数目(的相反数)更新
}
else//r1为小树
{
sets[r1].parent=r2;//小树的根挂在大树根上
sets[r2].parent=temp;//大树元素数目(的相反数)更新
}
}
}
//判断是否在同一等价类
bool UFSets::Differ(char a,char b)
{
if(Find(a)==-1||Find(b)==-1)
return 0;//有一个找不到都返回0
return Find(a)==Find(b) ? 1 : 0;//都找到且相等返回1
}
测试程序
#include "UFSets.h"
int main()
{
char a[]={'A','B','C','D',
'E','F','G','H','I','J'};
UFSets b(a,10);
char k;
char m,n;
while(1)
{
cout<<endl<<"1.合并";
cout<<endl<<"2.判同";
cout<<endl<<"3.查找";
cout<<endl<<"4.显示";
cout<<endl<<"其它.结束"<<endl;
cin>>k;
switch(k)
{
case '1':
cout<<"输入char类型m n:";
cin>>m>>n;
b.Union(m,n);
break;
case '2':
cout<<"输入char类型m n:";
cin>>m>>n;
if(b.Differ(m,n)==1)
cout<<"YES";
else
cout<<"NO or N/A";
break;
case '3':
cout<<"输入char类型m:";
cin>>m;
cout<<b.Find(m);
break;
case '4':
b.Show();
break;
default:
cout<<"程序结束";
return 0;
}
}
}
运行结果: