关于我对并查集的一些思考

本人大一,一开始接触ACM,想要争取到一个好点的名次。但总是心有余力不足,特此在这里写下自己关于算法知识和数据结构的理解。借以勉励自己!!!

并查集!!!

目录

我的理解.

具体的实现方式

代码示范

我的思考

关于做题时的一些醒悟()


我的理解.

首先并查集本身可以看成是一种树形结构。那么,既然是树形结构,就可以使用递归实现创建并查集。在我看了就是通过递归实现并查集的改变和创建。因为是树形结构,那么一开始的每个结点都可以看成是一棵只有一个结点的树。所以在树的移栽过程中,就实现了树的合并(有点费解)。但是就是这个意思。

另外并查集主要解决的是两个元素之间是不是有某种共同属性(比如说 你遇到你哥 哎!你就知道你俩是一家人) 另外还有元素之间的合并,也就是让不具有共同属性的两个元素让他们具有共同元素(比如说你在外面遇到一只流浪猫,心疼猫猫,就把它带回来家,得到了共同的属性——成为一家人)

具体的实现方式

首先并查集是有模板的

首先有几个原则

1,我们使用数组实现记录树的结点的元素,所以空间密度比较低。

2.使用树形结构创建并查集,看是否是同意属性,需要看两个元素是否是同一个树(鉴定方法就是看元素所在树的根结点是否相同)

3.一开始是让所有的元素都为他自己的下标值,我们默认数组元素的内容是数组下标值的元素本身就是这棵树的根节点。

4.每一个根节点就是区别这个树与别的树之间的关键区别,所以根节点也叫做代表元。比较节点所在树的代表元,就可以实现比较。但是每个结点怎么找到自己的代表元,只需要每个结点的内容存储自己的父节点的下标值,就可以实现索引。

5.通过4可以知道,只有每个结点存储自己在树中的父节点的下标值,去找到父节点的位置,再根据父节点位置找爷爷节点,直到找到根节点。但是这样的效率太慢了,不如直接让孙子节点指向代表元。这样就可以了。

7.实现树的移植,让两个树的根节点成为一个父子结点的关系,就可以实现两个树的所有元素都是一家人!!!!

6.为了实现树的移植 我们需要尽量让树的深度不要增加(因为树太深了就会费时间),但是所以我们需要记录每个元素所在树的深度(使用rank[]数组)。越靠向根节点,结点的深度越大。在比较之后总是把根节点深度大的做为树的移植的父节点。

代码示范

Come On My Code!!!!!!

void init(const int n){//实现一开始的初始化 初始化n个结点
	for(int i=0;i<n;i++){
		pre[i]=i; //这个是让每个元素都是一个根节点 之后只要让树移植到别的树就可以了
		rank[i]=1;//rank就是为了记录每棵树的每个结点的深度
	}//一开始都是一棵只有一个结点的树 所以深度记录为 1
}

接下来时find()函数 找到自己的父节点!(参考引文【算法与数据结构】—— 并查集

int find(int x)     	 		    //查找结点 x的根结点 
{
    if(pre[x] == x) return x;  		//递归出口:x的上级为 x本身,则 x为根结点 
    return find(pre[x]); 			//递归查找 
} 
 
//下面这个是改进后的查找算法
int find(int x)     				//改进查找算法:完成路径压缩,将 x的上级直接变为根结点,那么树的高度就会大大降低 
{/
    if(pre[x] == x) return x;		//递归出口:x的上级为 x本身,即 x为根结点 
    return pre[x] = find(pre[x]);   //此代码相当于先找到根结点 rootx,然后 pre[x]=rootx 
} //但是改进后的算法本身还是要依靠原先的路径实现一遍找到根节点,在找的过程中让这些经过的节点都指向根节点。

关于合并两颗树。先比较两个树的根节点的深度大小 ,然后再将大的结点作为小的父节点。

bool join(int x,int y)
{
    x = find(x);						//寻找 x的代表元
    y = find(y);						//寻找 y的代表元
    if(x == y) return false;			//如果 x和 y的代表元一致,说明他们共属同一集合,则不需要合并,返回 false,表示合并失败;否则,执行下面的逻辑
    if(rank[x] > rank[y]) pre[y]=x;		//如果 x的高度大于 y,则令 y的上级为 x
    else								//否则
    {
        if(rank[x]==rank[y]) rank[y]++;	//如果 x的高度和 y的高度相同,则令 y的高度加1
        pre[x]=y;						//让 x的上级为 y
	}
	return true;						//返回 true,表示合并成功
}

我的思考

首先并查集可以实现,多个元素直接找到自己的归宿。知道自己是那家的,自己的祖宗结点(根节点 )是哪一个。但是怎么样去解决多个元素同时属于不同的集合?(就不如说我家的宠狗既是我家的,但也是别人家的。毕竟它是流浪狗  这个怎么去实现。但是一般来说,大多数的题都是在两家之间的共同元素实现联合(也就是我家和狗主人家因为这个狗而相互认识,并且之后亲如一家???(好奇怪)) 但是就是这个意思。

可是这个只是在题目中出现过,如果真的向我所说的,两家不会因为狗而相互认识。那有该怎样处理元素之间的数据关系呢?

关于做题时的一些醒悟()

1.CodeForces 277A Learning Language 

这道题就是要辨别两个员工是否是可以互相交流的(这个很重要)(注意如果A可以和B交流也可也和C交流,那么A,B,C三个人就共同掌握了彼此的所有语言——就好比我因为捡来的 流浪狗和狗主人家亲如一家人一样申请)

但是怎去判断两个人是否是可以交流呢,就只要看一下双方的语言中有没有重复(就是看有没有直接能交流的语言) 或许两个一开始是没有的。但是我如果通过对每个语言进行遍历,就可以找出有直接共同语言的两个人,再将这两个人通过join()函数实现合并。在遍历完所有语言之后,就实现了人员的合并。 然后找到不相交集合的个数sum,输出sum-1就可以啦!!!

之后就是判断一些特殊情况,比如说如果所有人都不会语言(咋能不会呢?难道是哑巴吗?)这种情况就要输出总人数。

2.HDU 1213 - How Many Tables

这个题 借鉴上面的这道题的思路,就可以完成了。事实上,比上面还要简单,因为不用判断是不是一家人了!!!!

未完待续。。。。。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值