《算法》-1.5-并查集算法

《算法》-1.5-并查集算法并查集 union find 算法并查集算法真的是简单却又非常实用的一种算法 要想理解这个算法可以用一个非常生动的例子说明 叫解密犯罪团伙 这是从 啊哈算法 看到的 讲解的非常生动形象 在一堆数量为 n 的犯人里 如果 n1 和 n2 是团伙 n2 又和 n3 是一伙 诸如此类的线索还有很多 问你 这 n 个人里有多少个犯罪团伙 其实还有个例子可以非常清楚到位的概括这个模型 那就是平面上有 n 个点 给定一条边既可以将

并查集(union-find)算法

并查集算法真的是简单却又非常实用的一种算法,要想理解这个算法可以用一个非常生动的例子说明,叫解密犯罪团伙,这是从《啊哈算法》看到的,讲解的非常生动形象。在一堆数量为n的犯人里,如果n1和n2是团伙,n2又和n3是一伙,诸如此类的线索还有很多,问你,这n个人里有多少个犯罪团伙?其实还有个例子可以非常清楚到位的概括这个模型,那就是平面上有n个点,给定一条边既可以将两个点联通起来,现在有大量的这样的边,问最后有多少个连通子图?

为了计算无向图中的联通子图问题,并查集算法就能很有效的完成这个任务,还可以查询到任意两个顶点是否连通,下面是实现的代码;


首先要明白并查集能干什么?以下是并查集的功能,或者是我们使用并查集的需求所在;

  • 从一堆连接任意两个顶点的边信息中创建并查集
  • 可以查询到任意两个顶点是否连通?
  • 对于指定边能够将该边的两个顶点连通起来让他们属于同一个子图
  • 能够找到某一个顶点所在子图的根在哪儿
  • 计算一个图中的连通分量有多少个?

union find算法实现

下面给出并查集算法的简易api如下

public class UF UF(int n); // 创建包含n个顶点的并查集 void union(int p, int q); // 连接顶点p和q int find(int p); // 查找p所在分量的标识符 boolean connect(int p, int q); // 判别两个顶点是否连通 int count(); //返回图中总共的连通分量数目

下面是测试实例和具体代码实现;

package PAT; import java.util.Scanner; public class UF { 
     private int count; private int[] id; public UF(int n) { id = new int[n]; for(int i=0; i 
   
     public 
    static 
    void 
    main(String[] args) { Scanner s = 
    new Scanner(System.in); 
    int n = s.nextInt(); UF test = 
    new UF(n); 
    int m = s.nextInt(); 
    for( 
    int i= 
    0; i 
    
      int p = s.nextInt(); 
     int q = s.nextInt(); 
     if(test.connected(p, q)) 
     continue; test.union(p ,q); } } 
     private 
     void 
     union( 
     int p, 
     int q) { 
     int r1 = find(p); 
     int r2 = find(q); 
     if(r1 == r2) 
     return; id[r1] = r2; count --; } 
     private 
     boolean 
     connected( 
     int p, 
     int q) { 
     return find(p) == find(q); } 
     private 
     int 
     find( 
     int p) { 
     while(p != id[p]) p = id[p]; 
     return p; } } 
     
   

下面是测试数据:

10 11 4 3 3 8 6 5 9 4 2 1 8 9 5 0 7 2 6 1 1 0 6 7

测试结果如下:

分量有2true

思考?

实际上上面的union-find算法在id[]数组里创建了一棵树,这棵树有根节点,有叶节点,有中间节点,还有多颗树,这里面核心就是快速查找到某一个顶点的根节点是哪个节点,无论是在的过程,或者是将要的过程中,核心都在find函数中,那么有什么方法能使我的找根节点的时候更快么?这句话翻译一下,就是怎么降低这颗树的高度呢?只要树的高度降低了,我就可以很快的查到两个顶点的根节点是否是同一个,这将大大提高并查集算法用于查询的效率,所以为了做到这一点,便有了加权以后的并查集算法,她的核心思想不在改变find上,而在于union方法里,上述的union方法中,默认是将p的根节点变成q的根节点,也即将p所在的子树,依附到q所在的树上去,这样做是不加思考的,因为很可能导致q所在树越来越长,变成一颗极为不平衡的树,为了解决这个问题,我们只需要在union方法中,通过权重进行判别,如果q树比p树更高,那么就让p树依附到q树去,反之就让q树依附到p树去,每次都保证是小树依附到大树上去,这样构建的树就为较为平衡的树,这样对于任意节点,在搜索根节点过程中,效率将大大提高,并查集算法得到优化;

下面附上具体实现的代码:

package PAT; import java.util.Arrays; import java.util.Scanner; public class UF { 
      private int count; private int[] id, size; public UF(int n) { id = new int[n]; size = new int[n]; for(int i=0; i 
    
      1; } count = n; } 
     public 
     static 
     void 
     main(String[] args) { Scanner s = 
     new Scanner(System.in); 
     int n = s.nextInt(); UF test = 
     new UF(n); 
     int m = s.nextInt(); 
     for( 
     int i= 
     0; i 
     
       int p = s.nextInt(); 
      int q = s.nextInt(); 
      if(test.connected(p, q)) 
      continue; test.union(p ,q); } System.out.println(Arrays.toString(test.id)); System.out.println( 
      "分量有" + test.count + 
      "个"); System.out.println(test.connected( 
      2, 
      5)); } 
      private 
      void 
      union( 
      int p, 
      int q) { 
      int r1 = find(p); 
      int r2 = find(q); 
      if(r1 == r2) 
      return; 
      if(size[r1] < size[r2]) { id[r1] = r2; size[r2] += size[r1]; } 
      else { id[r2] = r1; size[r1] += size[r2]; } count --; } 
      private 
      boolean 
      connected( 
      int p, 
      int q) { 
      return find(p) == find(q); } 
      private 
      int 
      find( 
      int p) { 
      while(p != id[p]) p = id[p]; 
      return p; } } 
      
    

测试数据

8 7 0 1 1 2 3 4 3 5 3 6 3 7 2 7

输出结果:

[3, 0, 0, 3, 3, 3, 3, 3] 分量有1true

在我看来,这样的平衡调整,可以更好!为什么不能是下图这样呢?

这里写图片描述

而想要做到这样的优化,并非难事,只要将union函数改成下述即可:

 private void union(int p, int q) { int r1 = find(p); int r2 = find(q); if(r1 == r2) return; if(size[r1] < size[r2]) { id[r1] = r2; if(size[r1] + 1 > size[r2]) size[r2] += size[r1]; } else { id[r2] = r1; if(size[r2] + 1 > size[r1]) size[r1] = size[r2] + 1; } count --; }

多加一个判定即可,笔者认为这样构建的树就比上一个小树依附大树的效果要好的多,因为这里size数组里寸的不是该树的所有顶点个数,而是实实在在的最大高度,这是最关键的信息。不是让一个瘦子去依附胖子,而是让一个小个去依附高个儿。

测试数据如下:

8 7 0 1 1 2 3 4 3 5 3 6 3 7 2 7

输出结果:

[0, 0, 0, 0, 3, 3, 3, 3] 分量有1true

并查集算法经常出现在很多图的问题中,不过常常是作为解题的一小步,不过这一小步也是非常的重要。

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请联系我们举报,一经查实,本站将立刻删除。

发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/228298.html原文链接:https://javaforall.net

(0)
上一篇 2026年3月16日 下午7:21
下一篇 2026年3月16日 下午7:21


相关推荐

  • OpenClaw 2026 Skills命令完全指南:自定义技能从零写到自动执行

    OpenClaw 2026 Skills命令完全指南:自定义技能从零写到自动执行

    2026年3月13日
    3
  • 微信API接口大全「建议收藏」

    微信API接口大全「建议收藏」微信API接口1、基础消息类型1、客户端发送的心跳包HeartBeatReq=1001;2、消息接收确认回复(接收或拒绝接收)MsgReceivedAck=1002;3、错误单独提升为一种消息类型Error=1003;4、通用任务执行结果通知TaskResultNotice=1025;2、设备客户端授权类消息1、设备(手机客户端、客服客户端)获取通信token请求与响应DeviceAuthReq=1010;设备(手机客户端、客服客户端)获取通信token响应D…

    2022年10月2日
    8
  • 微信小程序云开发入门详细教程

    微信小程序云开发入门详细教程本篇是 从零开始 Python 微信小程序开发 专栏第九篇 主要介绍最新最全的云开发入门教程 微信小程序云开发 云函数 云数据库学习 微信小程序云开发扩展功能学习 希望大家能够一起学习 互相交流 一 认识小程序云开发 1 云开发简介小程序云开发是微信团队联合腾讯云推出的专业的小程序开发服务 开发者可以使用云开发快速开发小程序 小游戏 公众号网页等 并且原生打通微信开放能力 开发者无需搭建服务器 可免鉴权直接使用平台提供的 API 进行业务开发 云开发提供三大基础能力 帮助开发者快速开发

    2026年3月19日
    2
  • 大数据与云计算,物联网三者的区别和关联是_云计算侧重于数据分析

    大数据与云计算,物联网三者的区别和关联是_云计算侧重于数据分析大数据与云计算  为解决互联网应用对大规模计算能力、数据存储能力的迫切需求,云计算的概念被提出。云计算是一种分布式计算平台,通过虚拟技术将海量的硬件资源和虚拟资源虚拟成虚拟资源池,并根据需求任务的大小,向虚拟资源池获取相应的计算和存储资源。  在大数据处理的需求下,出现了许多优秀的云计算平台,例如Apache开源的Hadoop、Google的MapReduce、微软的Dryad等。  在处…

    2022年10月6日
    6
  • vs单步调试及断点调试基本介绍(入门版详细图文介绍)

    vs单步调试及断点调试基本介绍(入门版详细图文介绍)简述:本文面向小萌新简单描述visualstudio2019下的基本调试技巧1:打断点,在侧栏点击一下,即可生成断点功能:在调试时可以运行到这一步之后停止如图2:进而可以单步调试,快捷键f11//注,电脑快捷键分软件和系统层快捷键//本人戴尔G3是通过Ese+fn键切换,不同电脑可能不一样注意窗口i的值,进入第一次for循环,i赋值为0之后,进入printf,然后返回f…

    2022年5月22日
    184
  • lnmp一键安装的卸载

    lnmp一键安装的卸载

    2021年10月13日
    42

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注全栈程序员社区公众号