【算法基础实验】图论-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

参考资料

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

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

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【C++杂货铺】多态

目录 &#x1f308;前言&#x1f308; &#x1f4c1;多态的概念 &#x1f4c1; 多态的定义及实现 &#x1f4c2; 多态的构成条件 &#x1f4c2; 虚函数 &#x1f4c2; 虚函数重写 &#x1f4c2; C11 override 和 final &#x1f4c2; 重载&#xff0c;覆盖&#xff08;重写…

组合模式【结构型模式C++】

1.概述 组合模式又叫部分整体模式属于结构型模式&#xff0c;是用于把一组相似的对象当作一个单一的对象。组合模式依据树形结构来组合对象&#xff0c;用来表示部分以及整体层次。 2.结构 组件&#xff08;Component&#xff09;:定义了组合中所有对象的通用接口&#xff0c…

Kafka 3.x.x 入门到精通(06)Kafka进阶

Kafka 3.x.x 入门到精通&#xff08;06&#xff09;——对标尚硅谷Kafka教程 3. Kafka进阶3.1 Controller选举3.2 Broker上线下线3.3 数据偏移量定位3.4 Topic删除3.5 日志清理和压缩3.7 页缓存3.8 零拷贝3.9 顺写日志3.10 Linux集群部署3.10.1 集群规划3.10.2 安装虚拟机(略)3…

颠覆传统:机器人与AI大模型的结合,开启智能自动化的黄金时代!

引言&#xff1a;机器人技术与大模型的结合趋势 随着科技的迅速发展&#xff0c;机器人技术与大模型的结合已经成为必然趋势。这种结合不仅仅是技术的简单叠加&#xff0c;而是一种深层次的互补与融合&#xff0c;为机器人技术的应用开辟了新的可能性。大模型&#xff0c;能够…

4 -26

4-26 1 英语单词100个一篇六级翻译 2 div 4 补题目 3 概率论期中卷子一张&#xff0c;复习复习。 4 备课ing 晚上出去炫饭&#xff0c;串串香&#xff0c;无敌了。 中间一些模拟题是真的恶心&#xff0c;思维题是真的想不到&#xff0c;感觉自己就是一个废物呢。 1.是将一个数…

Postman 在 Linux 上的安装指南:简单快速开始使用

本文将介绍如何在 Linux 上安装 Postman 的详细步骤&#xff0c;Postman 支持的 Linux 的发行版包括&#xff1a;Ubuntu 14.04 及更高版本&#xff0c;Fedora 24&#xff0c;Debian 8 及更高版本。下面将介绍其具体的安装方法。 手动安装 Postman 的下载地址&#xff0c;下载…

线性代数:抽象向量空间

一、说明 有些函数系列极具线性代数的向量特征。这里谈及多项式构成函数的线性代数意义。问题是这个主题能展开多少内涵&#xff1f;请看本文的论述。 二、线性空间和向量 让我先问你一个简单的问题。什么是向量&#xff1f;为了方便起见&#xff0c;二维箭头从根本上说是平…

Synchronized关键字的深入分析

一、引言 在多线程编程中&#xff0c;正确地管理并发是确保程序正确运行的关键。Java提供了多种同步工具&#xff0c;其中synchronized关键字是最基本且最常用的同步机制之一。本文旨在深入解析synchronized的实现原理&#xff0c;探讨其在不同应用场景中的使用&#xff0c;并…

vivado 使用“链路 (Links)”窗口查看和更改链路设置

使用“链路 (Links) ”窗口查看和更改链路设置 创建链路后 &#xff0c; 就会将其添加到“ Links ”视图 &#xff08; 请参阅下图 &#xff09; 中 &#xff0c; 该视图是更改链路设置和查看状态的主要方法 &#xff0c; 也是最佳方法。 “ Links ”窗口中的每一行都对应 1 …

AI 语音机器人系统怎么搭建

搭建AI语音机器人系统通常包括以下几个关键步骤&#xff1a; 确定需求和技术选型&#xff1a;首先要明确AI语音机器人需要实现的功能&#xff0c;选择合适的技术框架和工具&#xff0c;如自然语言处理工具、语音识别工具等。 搜集和准备数据&#xff1a;收集和整理与业务相关…

一个不太好用的弹出层jquery.colorbox第二次点击不出来的解决方法

使用jquery.colorbox的时候第一次正常显示&#xff0c;但第二次的时候不显示。 需要先移除&#xff0c;再重新点击即可&#xff0c;代码如下 $.colorbox.remove();$.colorbox({href: url,iframe: true,width: options.width || 800,height: options.height || 600 });

Windows常见问题(技巧)总结

目录 问题Windows中更改快捷方式图标windows 中网速很慢,如何解决?因为Authenticated Users从其父系继承承权限&#xff0c;你无法删除此对象.Windows下的照片软件在哪个文件夹下?如何批量更改文件名?Windows 电脑pagefile.sys是什么文件,可以删除吗?U盘中打开文件时提示&a…