并查集详解 ——图文解说,简单易懂

2023-05-16

并查集是我暑假从高手那里学到的一招,觉得真是太精妙的设计了。以前我无法解决的一类问题竟然可以用如此简单高效的方法搞定。不分享出来真是对不起party了。(party:我靠,关我嘛事啊?我跟你很熟么?)

来看一个实例,HDU1232畅通工程

首先在地图上给你若干个城镇,这些城镇都可以看作点,然后告诉你哪些对城镇之间是有道路直接相连的。最后要解决的是整幅图的连通性问题。比如随意给你两个点,让你判断它们是否连通,或者问你整幅图一共有几个连通分支,也就是被分成了几个互相独立的块。像畅通工程这题,问还需要修几条路,实质就是求有几个连通分支。如果是1个连通分支,说明整幅图上的点都连起来了,不用再修路了;如果是2个连通分支,则只要再修1条路,从两个分支中各选一个点,把它们连起来,那么所有的点都是连起来的了;如果是3个连通分支,则只要再修两条路……

以下面这组数据输入数据来说明

4 2

1 3

4 3

第一行告诉你,一共有4个点,2条路。下面两行告诉你,1、3之间有条路,4、3之间有条路。那么整幅图就被分成了1-3-4和2两部分。只要再加一条路,把2和其他任意一个点连起来,畅通工程就实现了,那么这个这组数据的输出结果就是1。好了,现在编程实现这个功能吧,城镇有几百个,路有不知道多少条,而且可能有回路。 这可如何是好?

我以前也不会呀,自从用了并查集之后,嗨,效果还真好!我们全家都用它!

并查集由一个整数型的数组和两个函数构成。数组pre[]记录了每个点的前导点是什么,函数find是查找,join是合并。

int pre[1000 ];
int find(int x)             //查找根节点
{ 
    int r=x;
    while ( pre[r] != r )   //返回根节点 r
          r=pre[r];

    int i=x , j ;
    while( i != r )         //路径压缩
    {
         j = pre[ i ];      //在改变上级之前用临时变量  j 记录下他的值 
         pre[ i ]= r ;      //把上级改为根节点
         i=j;
    }
    return r ;
}
/*
判断x y是否连通,如果已经连通,就不用管了 如果不连通,就把它们所在的连通分支合并起,
*/
void join(int x,int y)                                      
{
    int fx=find(x),fy=find(y);
    if(fx!=fy)
        pre[fx ]=fy;
}

为了解释并查集的原理,我将举一个更有爱的例子。 话说江湖上散落着各式各样的大侠,有上千个之多。他们没有什么正当职业,整天背着剑在外面走来走去,碰到和自己不是一路人的,就免不了要打一架。但大侠们有一个优点就是讲义气,绝对不打自己的朋友。而且他们信奉“朋友的朋友就是我的朋友”,只要是能通过朋友关系串联起来的,不管拐了多少个弯,都认为是自己人。这样一来,江湖上就形成了一个一个的群落,通过两两之间的朋友关系串联起来。而不在同一个群落的人,无论如何都无法通过朋友关系连起来,于是就可以放心往死了打。但是两个原本互不相识的人,如何判断是否属于一个朋友圈呢?

我们可以在每个朋友圈内推举出一个比较有名望的人,作为该圈子的代表人物,这样,每个圈子就可以这样命名“齐达内朋友之队”“罗纳尔多朋友之队”……两人只要互相对一下自己的队长是不是同一个人,就可以确定敌友关系了。

但是还有问题啊,大侠们只知道自己直接的朋友是谁,很多人压根就不认识队长,要判断自己的队长是谁,只能漫无目的的通过朋友的朋友关系问下去:“你是不是队长?你是不是队长?”这样一来,队长面子上挂不住了,而且效率太低,还有可能陷入无限循环中。于是队长下令,重新组队。队内所有人实行分等级制度,形成树状结构,我队长就是根节点,下面分别是二级队员、三级队员。每个人只要记住自己的上级是谁就行了。遇到判断敌友的时候,只要一层层向上问,直到最高层,就可以在短时间内确定队长是谁了。由于我们关心的只是两个人之间是否连通,至于他们是如何连通的,以及每个圈子内部的结构是怎样的,甚至队长是谁,并不重要。所以我们可以放任队长随意重新组队,只要不搞错敌友关系就好了。于是,门派产生了。

这里写图片描述

下面我们来看并查集的实现。 int pre[1000]; 这个数组,记录了每个大侠的上级是谁。大侠们从1或者0开始编号(依据题意而定),pre[15]=3就表示15号大侠的上级是3号大侠。如果一个人的上级就是他自己,那说明他就是掌门人了,查找到此为止。也有孤家寡人自成一派的,比如欧阳锋,那么他的上级就是他自己。每个人都只认自己的上级。比如胡青牛同学只知道自己的上级是杨左使。张无忌是谁?不认识!要想知道自己的掌门是谁,只能一级级查上去。 find这个函数就是找掌门用的,意义再清楚不过了(路径压缩算法先不论,后面再说)。

int find(int x)             //查找我(x)的掌门
{
    int r=x;                //委托 r 去找掌门
    while (pre[r ]!=r)      //如果r的上级不是r自己(也就是说找到的大侠他不是掌门 = =)
    r=pre[r ] ;             //r就接着找他的上级,直到找到掌门为止。
    return  r ;             //掌门驾到~~~
}

再来看看join函数,就是在两个点之间连一条线,这样一来,原先它们所在的两个板块的所有点就都可以互通了。这在图上很好办,画条线就行了。但我们现在是用并查集来描述武林中的状况的,一共只有一个pre[]数组,该如何实现呢? 还是举江湖的例子,假设现在武林中的形势如图所示。虚竹小和尚与周芷若MM是我非常喜欢的两个人物,他们的终极boss分别是玄慈方丈和灭绝师太,那明显就是两个阵营了。我不希望他们互相打架,就对他俩说:“你们两位拉拉勾,做好朋友吧。”他们看在我的面子上,同意了。这一同意可非同小可,整个少林和峨眉派的人就不能打架了。这么重大的变化,可如何实现呀,要改动多少地方?其实非常简单,我对玄慈方丈说:“大师,麻烦你把你的上级改为灭绝师太吧。这样一来,两派原先的所有人员的终极boss都是师太,那还打个球啊!反正我们关心的只是连通性,门派内部的结构不要紧的。”玄慈一听肯定火大了:“我靠,凭什么是我变成她手下呀,怎么不反过来?我抗议!”抗议无效,上天安排的,最大。反正谁加入谁效果是一样的,我就随手指定了一个。这段函数的意思很明白了吧?

void join(int x,int y)          //我想让虚竹和周芷若做朋友
{
    int fx=find(x),fy=find(y);  //虚竹的老大是玄慈,芷若MM的老大是灭绝
    if(fx!=fy)                  //玄慈和灭绝显然不是同一个人
    pre[fx ]=fy;                //方丈只好委委屈屈地当了师太的手下啦
}

再来看看路径压缩算法。建立门派的过程是用join函数两个人两个人地连接起来的,谁当谁的手下完全随机。最后的树状结构会变成什么胎唇样,我也完全无法预计,一字长蛇阵也有可能。这样查找的效率就会比较低下。最理想的情况就是所有人的直接上级都是掌门,一共就两级结构,只要找一次就找到掌门了。哪怕不能完全做到,也最好尽量接近。这样就产生了路径压缩算法。 设想这样一个场景:两个互不相识的大侠碰面了,想知道能不能揍。 于是赶紧打电话问自己的上级:“你是不是掌门?” 上级说:“我不是呀,我的上级是谁谁谁,你问问他看看。” 一路问下去,原来两人的最终boss都是东厂曹公公。 “哎呀呀,原来是记己人,西礼西礼,在下三营六组白面葫芦娃!” “幸会幸会,在下九营十八组仙子狗尾巴花!” 两人高高兴兴地手拉手喝酒去了。 “等等等等,两位同学请留步,还有事情没完成呢!”我叫住他俩。 “哦,对了,还要做路径压缩。”两人醒悟。 白面葫芦娃打电话给他的上级六组长:“组长啊,我查过了,其习偶们的掌门是曹公公。不如偶们一起及接拜在曹公公手下吧,省得级别太低,以后查找掌门麻环。” “唔,有道理。” 白面葫芦娃接着打电话给刚才拜访过的三营长……仙子狗尾巴花也做了同样的事情。 这样,查询中所有涉及到的人物都聚集在曹公公的直接领导下。每次查询都做了优化处理,所以整个门派树的层数都会维持在比较低的水平上。路径压缩的代码,看得懂很好,看不懂也没关系,直接抄上用就行了。总之它所实现的功能就是这么个意思。

这里写图片描述

此文章转载自:

https://blog.csdn.net/liujian20150808/article/details/50848646

https://blog.csdn.net/wangyibo0201/article/details/51998273

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

并查集详解 ——图文解说,简单易懂 的相关文章

  • Ubuntu 16.04安装chrome稳定版

    1 打开终端 xff08 Ctrl 43 Alt 43 T xff09 xff0c 输入以下命令 xff1a sudo wget https repo fdzh org chrome google chrome list P etc apt
  • 【建议收藏】保姆级教程 - 图解Windows10+优麒麟双系统安装

    为防止超速翻车 xff0c 建议通读全文后再进行操作 xff08 此处双系统以 Windows 10 43 优麒麟 20 04 LTS Pro 为例 xff0c 其他版本的系统仅供参考 xff09 01 安装前的准备 四小步 第一步 xff
  • Linux文件系统--文件类型

    Linux中一切都是文件 xff0c 文件类型有多种 xff0c 使用ls l命令可以查看文件属性 xff0c 所显示结果的第一列的第一个字符用来表示文件类型 xff0c 如下 xff1a 1 普通文件 第一列第一个字符为 的文件为普通文件
  • Windows Terminal 快速配置 oh-my-posh

    背景 想美化下windows terminal xff0c 选择了oh my posh 网上的文章有点多 xff0c 加上官方的教程对初次使用着并不是太友好 xff0c 所以自己快速摸索了 记录下过程 步骤 1 xff0c 安装oh my
  • SVN彻底删除某版本的方法

    如果出现了提交失误 xff0c 想从服务器端彻底删除某版本 xff0c 可以这么做 在服务器端的Repository RepoName db revs 0和 db revprops 0中删除对应的版本号 xff0c 此时再update会出现
  • ZEMAX | 使用点扩散函数的衍射极限成像系统的分辨率

    ZEMAX 使用点扩散函数的衍射极限成像系统的分辨率 成像系统 xff08 例如显微镜 xff09 的衍射极限分辨率可以通过不同方式表征 在本文中 xff0c 我建议使用在 OpticStudio 中计算的点扩散函数 PSF 来客观衡量这些
  • 荐书 | 从启蒙到进阶,值得推荐的五本少儿编程

    据小木对身边的人了解 xff0c 好像码农们都有这么一个愿望 xff1a 等我有孩子了 xff0c 我一定教我的孩子学编程 玩游戏玩自己设计的才酷 xff01 看着一个个码农爸爸憧憬着美好的愿景 xff0c 小木恨不得马上帮他们实现这个愿望
  • 在浏览器中输入一个网址后,发生了什么?

    此文章转载自 xff1a https www cnblogs com SarahLiu p 5954832 html 这是面试中一道非常经典的问题 当你在浏览器中输入一个网址 xff0c 浏览器的处理过程如下 xff1a 第一步 浏览器查找
  • Banner基本使用 2.1.0

    Step 1 依赖banner Gradle dependencies compile 39 com youth banner banner 2 1 0 39 Step 2 添加权限到你的 AndroidManifest xml lt if
  • eclipse控制台变动难调整

  • 苹果cms详细安装方法

    做影视网站的站长对苹果cms是相当熟悉的 xff0c 毕竟这套系统实在太好用了 xff0c 使它一直火到了今天 xff01 今天小编就带着刚接触到本套程序的大家用它来搭建一次影视视频网站 xff01 程序运行环境 Apache 43 PHP
  • MySQL的版本以及版本号

    针对不同的用户 xff0c MySQL 分为两个版本 xff1a MySQL Community Server xff08 社区版 xff09 xff1a 该版本完全免费 xff0c 但是官方不提供技术支持 MySQL Enterprise
  • MySQL配置教程(图解版)

    配置 MySQL 数据库有两种比较常见的方式 xff0c 分别是使用配置向导和手动更改 xff0c 下面我们来分别介绍一下这两种方式 使用配置向导 步骤 1 xff1a MySQL 安装完成之后 xff0c 进行配置信息的确认 xff0c
  • MySQL常用运算符详解

    MySQL 数据库中的表结构确立后 xff0c 表中的数据代表的意义就已经确定 而通过 MySQL 运算符进行运算 xff0c 就可以获取到表结构以外的另一种数据 例如 xff0c 学生表中存在一个 birth 字段 xff0c 这个字段表
  • Java 文档注释

    Java 支持三种注释方式 前两种分别是 和 xff0c 第三种被称作说明注释 xff0c 它以 开始 xff0c 以 结束 说明注释允许你在程序中嵌入关于程序的信息 你可以使用 javadoc 工具软件来生成信息 xff0c 并输出到HT
  • JSP开发环境搭建(Tomcat的安装和配置)

    使用 JSP 开发程序 xff0c 需要具备对应的运行环境 xff1a Web 浏览器 Web 服务器 JDK 开发工具包 数据库 xff08 MySQL SQL Server 等 xff09 下面以 Windows 操作系统为平台介绍 J
  • JS字符串替换(使用replace()方法)

    replace 方法的第二个参数可以使用函数 xff0c 当匹配时会调用该函数 xff0c 函数的返回值将作为替换文本使用 xff0c 同时函数可以接收以 为前缀的特殊字符 xff0c 用来引用匹配文本的相关信息 约定字符串说明 1 2 9
  • Spring Boot项目搭建步骤(超详细)

    在 Spring Tools 4 for Eclipse 中依次选择 File gt New gt Maven Project xff0c 然后在出现的界面中按图 1 所示增加相关信息 图 1 创建 maven 项目 完了上述操作之后 xf
  • Java Swing JRadioButton:单选按钮组件

    单选按钮与复选框类似都有两种状态 xff0c 不同的是一组单选按钮中只能有一个处于选中状态 Swing 中 JRadioButton 类实现单选按钮 xff0c 它与 JCheckBox 一样都是从 JToggleButton 类派生出来的
  • Java Swing计算器界面的实现

    在本节之前已经详细介绍了 Swing 中容器 布局管理器以及常用的基本组件 本案例将综合运用这些知识实现一个计算器的布局 在本实例中使用两种布局管理器来进行界面设计 计算器界面可以分成两部分 xff0c 即显示区和键盘区 显示区可以使用文本

随机推荐