并查集之您可能认识的人

一 并查集

       什么是并查集呢?像队列、栈等数据结构,我们可以根据它们的特点给出定义,例如,栈是先进先后出的线性数据结构;队列是先进先出的线性数据结构,那么并查集是什么样的数据结构呢? 我们先将其“逐字分解”,分析出其处理数据的特点,最后在给出总结或定义。

并查集中,集是指集合,并和查是针对集合的操作。

“集”(即集合)的数学定义:集合是指具有某种特定性质的具体的或抽象的对象汇总而成的集体。其中,构成集合的这些对象则称为该集合的元素。 简言之,“集合是一堆元素构成的集体”

“并”操作:即数学中集合的并运算,例如,{1, 2} U {3 ,4)= {1, 2, 3, 4}。
“查”操作1. 查询某个元素所属的集合。由此,我们还可以引申出另一个和查询相关的操作,查询两个元素的所属集合是否相同,即 2.查询两个元素是否在同一个集合

综上,并查集是针对集合进行“并”和“查”两类操作的数据结构

二 应用

1. 连接问题

举个例子,在某短视频平台,我关注了我的室友,而过了两天我收到了平台的关注推送,“您可能认识的人某学妹”,这是怎么回事,在我的再三询问下,室友承认了他偷偷关注漂亮学妹A的事实!有这事竟然不告诉我,还好软件帮我。

“您可能认识的人”,这一功能是如何实现的呢?其实它很可能用到了我们今天讲解的数据结构——并查集。

具体做法,我们把具有关系的对象(这里对象产生的关系是关注,我和室友有关系,室友和学妹有关系,我和学妹存在间接的关系,只要对象间存在“路径”,不管直接、间接,都算是有联系)放入一个集合,如此,我们四个被放入了同一个集合。然后通过 “查”操作2.查询两个元素是否在同一个集合,得知我和学妹在同一个集合,即我们是有“关系”的,我和学妹之间可以通过某个“路径”产生连接。于是软件就把学妹推送给了我。

在这里插入图片描述
在短视频这个社交网络中,不可能只有我们几个人。我们Java老师和他开辅导班的对象英语老师也注册了短视频平台账号。由于我想深入学习下Java方面的相关知识,于是我关注了Java老师,如下图所示:
在这里插入图片描述
某天,室友收到了英语老师发来的“关于如何才能通过大学英语四级的广告”!英语老师偷偷关注了我的室友。碍于我在和Java老师学习的面子,室友报名了英语老师的课程。(其实,它已经是第四次每过了)

英语老师为什么知道我的室友呢?有了上面的介绍,应该可以很容易的理解。

首先,“我”和“Java老师”建立了关注关系(或者说连接),这是通过“并”操作实现的。开始时存在集合A={我,室友,学妹,学姐},集合B={Java老师,英语老师},由于“我”和“java”建立了联系,使得任意两个对象之间都可以通过“路径”产生关系(连接),所以所有对象属于同一个集合,A U B={我,室友,学妹,学姐,Java老师,英语老师}; 然后通过 “查”操作2.查询两个元素是否在同一个集合,得知室友和英语老师在同一个集合,即室友和英语老师可以产生连接,于是把室友推送给了英语老师,英语老师关注并给室友发来了四级辅导课程,到此,一切真相大白了。我通过室友认识了学妹,室友通过我认识了英语老师,达到了双赢!

综上,所谓连接问题,就是看两个节点是否存在某种联系(节点间存在路径,则存在联系),即可以通过并查集的“查”操作2.查询两个元素是否在同一个集合来判断.

对上面提到的短视频应用中存在的社交关注关系进一步抽象,可以把每个用户看成“节点”,关注关系看作“连接”,存在多组社交关注关系可以看做一个大的网络,于是,我们知道并查集可以很好的解决,网络中节点间的连接状态

举个例子,在交通网络中,A省和B省各自的市之间存在可以到达的关系,问从A省的a市是否可以到达B省的b市呢?

只有通过把A省和B省存在到达关系的市放入并查集中,然后通过并查集的查操作,就可以解决此问题。

2 数学中的集合类实现

主要是求集合的并集

三 数据结构的实现

实现原则:数据的存储结构要体现数据的逻辑结构

数据的逻辑结构如下所示:

集合A = {元素1, 元素2, 元素3}
集合B = {元素4, 元素5}

首先,明确一点,由并查集操作的特点可知,它不进行元素的添加和修改操作,主要是针对给定元素的集合合并和集合查询。

存储时既要存储元素,更重要的是要存储元素之间的关系,即元素之间属于同一集合的关系。

第一种实现,把集合看成线性结构
结点定义如下:
class Node<E>{
	private E e;
	private char aggregateNmae; // 字符表示的集合名
}

实际存储,使用了数组如图所示:在这里插入图片描述
并操作,只需修改节点的集合属性;查操作,只需遍历数组,查看某元素对应的集合属性。

可以看到节点的元素自从存储进数组开始从未进行过修改,索引和元素存在一种唯一的对应关系,与我们在数据库中见到的id类似,不妨将索引作为元素的id,数据库中id可以唯一代替一条数据,同理索引也可以唯一标识一个元素。

第一种实现的改进

将数组索引看做元素的id,即索引代表元素。索引位置只需存元素所属于的集合。集合的名字只是一种标识符而已,可以是任何的类型,为了方便,这里选择int类型作为集合名。

这样做有一个好处,不需要在创建节点对象来存储元素所在集合的信息,直接存储基本类型int即可表示。好处是,对于面向对象语言来说,创建对象是比较耗时的,特别是数据量特别大时。至于元素的存储,我们可以使用数组或者Map来实现(其实元素的存储这部分已经不是并查集数据结构要考虑的问题了),如此,当你想访问元素的相关信息时,可以根据id去查找对应元素。如下图所示:
在这里插入图片描述
现在,我们可以定义出并查集的接口了,主要包括一个并操作和两个查操作。

public interface UF {
    /**
     * 合并元素p和元素q所在集合
     * @param pId 元素p的id
     * @param qId 元素q的id
     */
    void union(int pId, int qId);

    /**
     * 查找元素所属的集合
     * @param id 元素的id
     * @return int类型的集合名
     */
    int find(int id);

    /**
     * 查看元素p和元素q是否在同一个结合(属于同一个集合,表示两者存在连接)
     * @param pId 元素p的id
     * @param qId 元素q的id
     * @return
     */
    boolean isConnected(int pId, int qId);
}

把集合看成线性结构,基于第一种实现的改进图示,进行实现(限于篇幅,具体实现文章末尾给出),时间复杂度如下,可以看到查操作是通过id(即数组索引)直接访问数组元素的,很快。而并操作,是需要修改两个集合中,其中一个集合的值的(修改为另一个集合的值,即表示在同一个集合了),其平均时间复杂度是O(n),所以这样实现方式称为Quick Find
在这里插入图片描述

第二种实现,把集合看成一颗树
在数据量比较大时,O(n)级别的时间复杂度,效率是很低的。为解决第一种实现中,并操作效率低的问题,因此提供了第二种实现。

集合的逻辑结构如下图所示:

在这里插入图片描述
此结构,存储的依然是元素对应的id,在同一棵树中的元素表示它们属于同一个集合。

底层实现如图所示:
在这里插入图片描述
似乎和第一种实现一样,下面我们将介绍该实现,并运算的实现过程,会让人对这样实现有更深的理解,体会到与第一种实现存在的本质区别

现在假设有三个集合:

A = {1} B = {2} C = {3}

在这里插入图片描述
不管是并操作还是查操作,都需要找到该元素的根结点,因此这种实现的时间复杂度如下所示,由于相对于第一种实现,查的效率是比较高的,因此称这种实现为Quick Union

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

明月几时有666

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值