【算法基础实验】图论-UnionFind连通性检测之quick-union

Union-Find连通性检测之quick-union

理论基础

在图论和计算机科学中,Union-Find 或并查集是一种用于处理一组元素分成的多个不相交集合(即连通分量)的情况,并能快速回答这组元素中任意两个元素是否在同一集合中的问题。Union-Find 特别适用于连通性问题,例如网络连接问题或确定图的连通分量。

Union-Find 的基本操作

Union-Find 数据结构支持两种基本操作:

  1. Union(合并): 将两个元素所在的集合合并成一个集合。
  2. Find(查找): 确定某个元素属于哪个集合,这通常涉及找到该集合的“代表元素”或“根元素”。

Union-Find 的结构

Union-Find 通常使用一个整数数组来表示,其中每个元素的值指向它的父节点,这样形成了一种树形结构。集合的“根元素”是其自己的父节点。

Union-Find 的优化技术

为了提高效率,Union-Find 实现中常用两种技术:

  1. 路径压缩(Path Compression): 在执行“查找”操作时,使路径上的每个节点都直接连接到根节点,从而压缩查找路径,减少后续操作的时间。
  2. 按秩合并(Union by Rank): 在执行“合并”操作时,总是将较小的树连接到较大的树的根节点上。这里的“秩”可以是树的深度或者树的大小。

应用示例

Union-Find 算法常用于处理动态连通性问题,如网络中的连接/断开问题或者图中连通分量的确定。例如,Kruskal 的最小生成树算法就使用 Union-Find 来选择边,以确保不形成环路。

总结

Union-Find 是解决连通性问题的一种非常高效的数据结构。它能够快速合并集合并快速判断元素之间的连通性。通过路径压缩和按秩合并的优化,Union-Find 在实际应用中可以接近常数时间完成操作。因此,它在算法竞赛、网络连接和社交网络分析等领域有广泛的应用。

数据结构

private int[] id // 分量id(以触点作为索引)
private int count // 分量数量

实验数据和算法流程

本实验使用tinyUF.txt作为实验数据,数据内容如下,一共定义了10对连通性关系

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

实验的目的是检测数据中共有多少个连通分量,并打印每个元素所属的连通分量编号

下图展示了处理5和9连通性的一个瞬间

请添加图片描述

完整流程如下

请添加图片描述

代码实现

原则是小树挂在大树下,如果一棵高度为1,但是有100个节点的树,要把高度为2的三节点小树挂在这课大树上

可以想象如果反过来,大树挂在小树下,大树的100个节点都将变成高度为3的树枝,这样的话查询的整体成本就太高了

import edu.princeton.cs.algs4.StdOut;
import edu.princeton.cs.algs4.StdIn;

public class myQuickUnion {
    private int[] id;
    private int count;
    private int finds;
    private int[] size;
    public myQuickUnion(int N) { // 初始化分量id数组
        count = N;
        id = new int[N];
        for (int i = 0; i < N; i++) id[i] = i;
        size = new int[N];
        for (int i = 0; i < N; i++) size[i] = 1;

    }
    public boolean connected(int p, int q)
    {return find(p) == find(q);}
    public int count()
    { return count;}
    private int find(int p){
        while(p != id[p]){
            p = id[p];
            finds ++;
        }
        return p;
    }
    public void union(int p, int q){
        int pRoot = find(p);
        int qRoot = find(q);
        if(pRoot==qRoot) return;
        if(size[pRoot]<size[qRoot])
            {id[pRoot]=qRoot;
             size[qRoot]+=size[pRoot];}
        else
            {id[qRoot]=pRoot;
             size[pRoot]+=size[qRoot];}
        //id[pRoot] = qRoot;
        //此处注释掉的是随机将两棵树的根连接的表达式
        //根据实测,加权时总的find次数为2000左右,普通union为2万次左右
        count --;
    }
    public static void main(String[] args){
        int N = StdIn.readInt();
        myQuickUnion qu = new myQuickUnion(N);
        while(!StdIn.isEmpty()){
            int p = StdIn.readInt();
            int q = StdIn.readInt();
            if(qu.connected(p,q)) continue;
            qu.union(p,q);
        }
        StdOut.println("components: "+qu.count);
        for(int i=0;i<N;i++){
            StdOut.println(i+":"+qu.id[i]);
        }
        StdOut.println("find counts: "+qu.finds);
    }
}

代码详解

这段代码是一个实现了“加权快速合并”(Weighted Quick Union)的并查集算法的Java类 myQuickUnion。该算法用于处理大量元素的动态连通性问题,提高了普通快速合并(Quick Union)算法的效率。以下是对这段代码的详细解释:

类定义和变量


public class myQuickUnion {
    private int[] id;     // id数组,用于保存每个节点的父节点
    private int count;    // 连通分量的数量
    private int finds;    // 进行find操作的次数统计
    private int[] size;   // 每个根节点相应的分量大小

  • id 数组中,每个位置保存了该位置元素的父节点索引。
  • count 记录当前图中连通分量的数量。
  • finds 用于记录执行 find 操作的次数,有助于分析算法性能。
  • size 数组用于保存以每个节点为根的树的大小。

构造函数


public myQuickUnion(int N) {
    count = N;
    id = new int[N];
    for (int i = 0; i < N; i++) id[i] = i;
    size = new int[N];
    for (int i = 0; i < N; i++) size[i] = 1;
}

构造函数初始化了 id 数组和 size 数组。id 数组的每个元素初始指向自身,表示每个元素都是自己的根节点。size 数组中的每个元素初始为 1,表示每个根节点的树大小为 1。

方法实现

connected


public boolean connected(int p, int q) {
    return find(p) == find(q);
}

检查两个元素是否连通,即它们是否有相同的根。

find


private int find(int p) {
    while (p != id[p]) {
        p = id[p];
        finds++;
    }
    return p;
}

找到元素 p 的根节点。这里使用了路径压缩的一种简单形式,在找根的过程中顺便统计操作次数。

union


public void union(int p, int q) {
    int pRoot = find(p);
    int qRoot = find(q);
    if (pRoot == qRoot) return;
    if (size[pRoot] < size[qRoot]) {
        id[pRoot] = qRoot;
        size[qRoot] += size[pRoot];
    } else {
        id[qRoot] = pRoot;
        size[pRoot] += size[qRoot];
    }
    count--;
}

合并两个元素所在的树。如果一个树的大小小于另一个,小的树的根节点将指向大的树的根节点,并更新树的大小。这种“按大小加权”的策略有助于减少树的高度,从而提高后续操作的效率。

主函数


public static void main(String[] args) {
    int N = StdIn.readInt();
    myQuickUnion qu = new myQuickUnion(N);
    while (!StdIn.isEmpty()) {
        int p = StdIn.readInt();
        int q = StdIn.readInt();
        if (qu.connected(p, q)) continue;
        qu.union(p, q);
    }
    StdOut.println("components: " + qu.count);
    for (int i = 0; i < N; i++) {
        StdOut.println(i + ":" + qu.id[i]);
    }
    StdOut.println("find counts: " + qu.finds);
}

在主函数中,从标准输入读取元素数量和成对的整数。每对整数代表一次尝试连接的操作。如果两个元素已经连通,则忽略;否则,进行合并操作。最终,输出连通分量的数量、每个元素的最终根,以及进行 find 操作的总次数。

实验

代码编译

$ javac myQuickUnion.java

代码运行

该算法处理tinyUF.txt时由于使用了加权方法,优先将小树挂在大树下,这样可以极大减少find操作的次数,提高了性能,在打印中可以看到find counts的值为13,即一共执行了13次find,

$ java myQuickUnion < ..\data\tinyUF.txt 
components: 2
0:6
1:2
2:6
3:4
4:4
5:6
6:6
7:2
8:4
9:4
find counts: 13

如果将权重处理注释掉,使用普通quick-union方法,find counts数值会变为16,影响性能

如果导入mediumUF.txt或者largeUF.txt数据,这个差距将更加悬殊

请添加图片描述

java myQuickUnion < ..\data\tinyUF.txt
components: 2
0:1
1:1
2:1
3:8
4:3
5:0
6:5
7:1
8:8
9:8
find counts: 16

参考资料

算法(第四版) 人民邮电出版社

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/583965.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

55.基于SpringBoot + Vue实现的前后端分离-旅游管理系统(项目 + 论文)

项目介绍 本站是一个B/S模式系统&#xff0c;采用SpringBoot Vue框架&#xff0c;MYSQL数据库设计开发&#xff0c;充分保证系统的稳定性。系统具有界面清晰、操作简单&#xff0c;功能齐全的特点&#xff0c;使得基于SpringBoot Vue技术的旅游管理系统设计与实现管理工作系统…

【Node.js工程师养成计划】之express框架

一、Express 官网&#xff1a;http://www.expressjs.com.cn express 是一个基于内置核心 http 模块的&#xff0c;一个第三方的包&#xff0c;专注于 web 服务器的构建。 Express 是一个简洁而灵活的 node.js Web应用框架, 提供了一系列强大特性帮助你创建各种 Web 应用&…

docker学习笔记3:VmWare CentOS7安装与静态ip配置

文章目录 一、安装CentOS71、下载centos镜像2、安装二、设置静态ip三、xshell连接centos本专栏的docker环境是在centos7里安装,因此首先需要会安装centos虚拟机。 本篇博客介绍如何在vm虚拟机里安装centos7。 一、安装CentOS7 1、下载centos镜像 推荐清华源,下载如下版本 …

使用量排名前50的GPTs趋势和特征

Chatgpt的gpt商店已经有几千gpts了。目前哪些gpts比较受欢迎呢&#xff1f;有哪些趋势和投资呢? 根据whatplugin.ai&#xff08;截止日期为2024年3月&#xff09;&#xff0c;使用量最多的50个gpts数据分析结果如下&#xff1a; GPTs类型的分布情况如下&#xff1a; 图像生成…

案例-部门管理-删除

黑马程序员JavaWeb开发教程 文章目录 一、查看页面原型二、查看接口文档三、开发1、Controller2、Service&#xff08;1&#xff09;service接口层&#xff08;3&#xff09;service实现层 3、Mapper4、Postman 一、查看页面原型 二、查看接口文档 三、开发 1、Controller 因…

Keepalived+LVS实现Nginx集群配置

Nginx1和Nginx2组成集群&#xff0c;为了实现负载均衡&#xff0c;在集群的前端配置了LVS服务&#xff0c;但是一台LVS容器产生单点故障&#xff0c;因此需要过Keepalived实现LVS的高可用集群 192.168.136.55node1keepalived192.168.136.56node2keeplived192.168.136.57 node3n…

Excel 中用于在一个范围中查找特定的值,并返回同一行中指定列的值 顺序不一样 可以处理吗

一、需求 Excel 中&#xff0c;在一列&#xff08;某范围内&#xff09;查找另一列特定的值&#xff0c;并返回同一行中另一指定列的值&#xff0c; 查找列和返回列的顺序不一样 二、 实现 1、下面是一个使用 INDEX 和 MATCH 函数的例子&#xff1a; 假设你有以下数据&…

CI/CD:基于kubernetes的Gitlab搭建

1. 项目目标 &#xff08;1&#xff09;熟悉使用k8s环境搭建Gitlab &#xff08;2&#xff09;熟练应用Gitlab基本配置 2. 项目准备 2.1. 规划节点 主机名 主机IP 节点规划 k8s-master 10.0.1.1 kube_master k8s-node1 10.0.1.2 kube_node k8s-node2 10.0.1.3 k…

影响外汇交易盈利的因素有哪些?

外汇交易就是通过汇率的差价来赚取相应的利润。在外汇交易中&#xff0c;投资者是否可以盈利&#xff0c;主要取决于是否正确的判断了市场趋势和行情。投资者在交易过程中受到主观和客观的因素影响&#xff0c;具体包含这些内容。 影响外汇交易盈利的因素有哪些&#xff1f; 1、…

【酱浦菌-爬虫项目】爬取学术堂论文信息

1. 首先&#xff0c;代码定义了一个名为 url 的变量&#xff0c;它是一个包含三个网址的集合&#xff08;或者说是一个集合的字典&#xff09;。这些网址分别是&#xff1a; - ‘http://www.xueshut.com/lwtimu/127966.html’ - ‘http://www.xueshut.com/lwtimu/12…

nmap扫描工控设备的脚本支持

参考资料 转自&#xff08;http://www.360doc.com/content/15/1201/11/26186435_517125254.shtml&#xff09; 介绍 NMAP是一款强大的网络扫描工具&#xff0c;除了普通的TCP/IP网络扫描之外&#xff0c;NMAP的扩展脚本功能为我们提供了更为广阔的应用范围。 针对脚本学习可…

Python使用设计模式中的建筑模式将数据写入Excel且满足条件内容标红

对于这个任务&#xff0c;适合使用"Builder"设计模式。Builder模式的主要目的是将对象的构建与其表示分离&#xff0c;以便相同的构建过程可以创建不同的表示。在这个情况下&#xff0c;我们需要一个构建器来逐行构建Excel表格&#xff0c;并根据给定的数据添加相应的…

C++中auto关键字的用法详解

1.简介 auto作为一个C语言就存在的关键字&#xff0c;在C语言和C之间却有很大区别。 在C语言中auto修饰的变量&#xff0c;是具有自动存储器的局部变量&#xff0c;但因为局部变量默认类别默认是auto修饰导致一直没有人去使用它。 C11中&#xff0c;标准委员会赋予了auto全新…

【MySQL | 第八篇】在MySQL中,如何定位慢查询以及对应解决方法?

文章目录 8.在MySQL中&#xff0c;如何定位慢查询以及对应解决方法&#xff1f;8.1MySQL慢查询日志8.1.1开启慢查询&#xff08;1&#xff09;修改配置文件&#xff08;2&#xff09;设置全局变量 8.1.2日志记录在表上&#xff08;实践&#xff09;8.1.3日志记录在文件上&#…

android studio 4.2.1运行java文件报错

当运行某个带main函数的java文件报这个错误的时候 Could not create task :app:Test.main(). > SourceSet with name main not found. 解决办法&#xff1a;在工程的.idea下的.gradlew.xml文件下添加 <option name"delegatedBuild" value"false"…

InternVL——GPT-4V 的开源替代方案

您的浏览器不支持 video 标签。 在人工智能领域&#xff0c;InternVL 无疑是一颗耀眼的新星。它被认为是最接近 GPT-4V 表现的可商用开源模型&#xff0c;为我们带来了许多惊喜。 InternVL 具备强大的功能&#xff0c;不仅能够处理图像和文本数据&#xff0c;还能精妙地理解…

基于H.264的RTP打包中的组合封包以及分片封包结构图简介及抓包分析

H.264视频流的RTP封装类型分析&#xff1a; 前言&#xff1a; NULL Hearder简介(结构如下)&#xff1a; ---------------|0|1|2|3|4|5|6|7|--------|F|NRI| Type |--------------- F&#xff1a;forbidden_zero_bit&#xff0c; 占1位&#xff0c;在 H.264 规范中规定了这…

Python数据分析大作业(ARIMA 自回归积分滑动平均模型) 4000+字 图文分析文档 销售价格库存分析+完整python代码

资源地址&#xff1a;Python数据分析大作业 4000字 图文分析文档 销售分析 完整python代码 完整代码分析 ​ 同时销售量后1000的sku品类占比中&#xff08;不畅销产品&#xff09;如上&#xff0c;精品类产品占比第一&#xff0c;达到66.7%&#xff0c;其次是香化类产品&#x…

【架构】后端项目如何分层及分层领域模型简化

文章目录 一. 如何分层1. 阿里规范2. 具体案例分析 二. 分层领域模型的转换1. 阿里规范2. 模型种类简化分析 三. 小结 本文描述后端项目中如何进行分层&#xff0c;以及分层领域模型简化 一. 如何分层 1. 阿里规范 阿里的编码规范中约束分层逻辑如下: 开放接口层&#xff1a…

Apache Seata基于改良版雪花算法的分布式UUID生成器分析1

title: Seata基于改良版雪花算法的分布式UUID生成器分析 author: selfishlover keywords: [Seata, snowflake, UUID] date: 2021/05/08 本文来自 Apache Seata官方文档&#xff0c;欢迎访问官网&#xff0c;查看更多深度文章。 Seata基于改良版雪花算法的分布式UUID生成器分析…