【数据学习结构笔记】8:并查集:并时加权/查后折叠

并查集可以看成一个多棵树组成的森林,它用双亲表示数组来存储。属同一棵树的元素属于同一等价类。

加权规则:在合并(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;
		}
	}
}

运行结果:

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值