数据结构09—— 并查集(Union-Find)
一、关于并查集
并查集(Union-Find)是一种树型的数据结构,常用于处理一些不相交集合(Disjoint Sets)的合并及查询问题。并查集(Union-Find)从名字可以看出,主要它涉及两种基本操作:合并和查找。这说明,初始时并查集中的元素是不相交的,经过一系列的基本操作(Union),最终合并成一个大的集合。
二、并查集的设计和基本实现
1.并查集接口的设计
public interface UF {
int getSize();
boolean isConnected(int p, int q);
void unionElements(int p, int q);
}
2.第一版本的并查集实现:基于数组
package com.zfy.uf;
public class UnionFind1 implements UF {
private int[] id; // 我们的第一版Union-Find本质就是一个数组
public UnionFind1(int size) {
id = new int[size];
// 初始化, 每一个id[i]指向自己, 没有合并的元素
for (int i = 0; i < id.length; i++) {
id[i] = i;
}
}
@Override
public int getSize() {
return id.length;
}
// 查找元素p所对应的集合编号
// O(1)复杂度
private int find(int p) {
if (p < 0 || p >= id.length)
throw new IllegalArgumentException("p is out of bound.");
return id[p];
}
// 查看元素p和元素q是否所属一个集合
// O(1)复杂度
@Override
public boolean isConnected(int p, int q) {
return find(p) == find(q);
}
// 合并元素p和元素q所属的集合
// O(n) 复杂度
@Override
public void unionElements(int p, int q) {
int pID = find(p);
int qID = find(q);
if (pID == qID) {
return;
}
for (int i = 0; i < id.length; i++) {
// 合并过程需要遍历一遍所有元素, 将两个元素的所属集合编号合并
if (id[i] == pID) {
id[i] = qID;
}
}
}
}
3.第二版的并查集
package com.zfy.uf;
public class UnionFind2 implements UF {
// 我们的第二版Union-Find, 使用一个数组构建一棵指向父节点的树
// parent[i]表示第一个元素所指向的父节点
private int[] parent;
// 构造函数
public UnionFind2(int size) {
parent = new int[size];
// 初始化, 每一个parent[i]指向自己, 表示每一个元素自己自成一个集合
for (int i = 0; i < size; i++)
parent[i] = i;
}
@Override
public int getSize() {
return parent.length;
}
// 查找过程, 查找元素p所对应的集合编号
// O(h)复杂度, h为树的高度
private int find(int p) {
if (p < 0 || p >= parent.length)
throw new IllegalArgumentException("p is out of bound.");
// 不断去查询自己的父亲节点, 直到到达根节点
// 根节点的特点: parent[p] == p
while (p != parent[p])
p = parent[p];
return p;
}
// 查看元素p和元素q是否所属一个集合
// O(h)复杂度, h为树的高度
@Override
public boolean isConnected(int p, int q) {
return find(p) == find(q);
}
// 合并元素p和元素q所属的集合
// O(h)复杂度, h为树的高度
@Override
public void unionElements(int p, int q) {
int pRoot = find(p);
int qRoot = find(q);
if (pRoot == qRoot)
return;
parent[pRoot] = qRoot;
}
}
4. 基于size的优化
package com.zfy.arithmetic.unionfnd; /**
* Created by vincent on 2018/10/15 上午10:13
*/
public class UnionFind3 implements UF { private int[] parent; // parent[i]表示第一个元素所指向的父节点
private int[] sz; // sz[i]表示以i为根的集合中元素个数 // 构造函数
public UnionFind3(int size){ parent = new int[size];
sz = new int[size]; // 初始化, 每一个parent[i]指向自己, 表示每一个元素自己自成一个集合
for(int i = 0 ; i < size ; i ++){
parent[i] = i;
sz[i] = 1;
}
} @Override
public int getSize(){
return parent.length;
} // 查找过程, 查找元素p所对应的集合编号
// O(h)复杂度, h为树的高度
private int find(int p){
if(p < 0 || p >= parent.length)
throw new IllegalArgumentException("p is out of bound."); // 不断去查询自己的父亲节点, 直到到达根节点
// 根节点的特点: parent[p] == p
while( p != parent[p] )
p = parent[p];
return p;
} // 查看元素p和元素q是否所属一个集合
// O(h)复杂度, h为树的高度
@Override
public boolean isConnected( int p , int q ){
return find(p) == find(q);
} // 合并元素p和元素q所属的集合
// O(h)复杂度, h为树的高度
@Override
public void unionElements(int p, int q){ int pRoot = find(p);
int qRoot = find(q); if(pRoot == qRoot)
return; // 根据两个元素所在树的元素个数不同判断合并方向
// 将元素个数少的集合合并到元素个数多的集合上
if(sz[pRoot] < sz[qRoot]){
parent[pRoot] = qRoot;
sz[qRoot] += sz[pRoot];
}
else{ // sz[qRoot] <= sz[pRoot]
parent[qRoot] = pRoot;
sz[pRoot] += sz[qRoot];
}
}
}
5.Main测试方法
package com.zfy.arithmetic.unionfnd;
import java.util.Random;
public class Main {
private static double testUF(UF uf, int m){
int size = uf.getSize();
Random random = new Random();
long startTime = System.nanoTime();
for(int i = 0 ; i < m ; i ++){
int a = random.nextInt(size);
int b = random.nextInt(size);
uf.unionElements(a, b);
}
for(int i = 0 ; i < m ; i ++){
int a = random.nextInt(size);
int b = random.nextInt(size);
uf.isConnected(a, b);
}
long endTime = System.nanoTime();
return (endTime - startTime) / 1000000000.0;
}
public static void main(String[] args) {
// UnionFind1 慢于 UnionFind2
// int size = 100000;
// int m = 10000;
// UnionFind2 慢于 UnionFind1, 但UnionFind3最快
int size = 100000;
int m = 100000;
UnionFind1 uf1 = new UnionFind1(size);
System.out.println("UnionFind1 : " + testUF(uf1, m) + " s");
UnionFind2 uf2 = new UnionFind2(size);
System.out.println("UnionFind2 : " + testUF(uf2, m) + " s");
UnionFind3 uf3 = new UnionFind3(size);
System.out.println("UnionFind3 : " + testUF(uf3, m) + " s");
}
}
测试结果:
因为本人太懒,一直没有画图,以后一定会改进!!!!!
结束语:合抱之木,生于毫末;九层之台,起于累土;千里之行,始于足下。
参考:bobobo老师的玩转数据结构
版权声明:尊重博主原创文章,转载请注明出处 https://www.cnblogs.com/hsdy
数据结构09—— 并查集(Union-Find)的更多相关文章
- 数据结构之并查集Union-Find Sets
1. 概述 并查集(Disjoint set或者Union-find set)是一种树型的数据结构,常用于处理一些不相交集合(Disjoint Sets)的合并及查询问题. 2. 基本操作 并查集 ...
- 数据结构 之 并查集(Disjoint Set)
一.并查集的概念: 首先,为了引出并查集,先介绍几个概念: 1.等价关系(Equivalent Relation) 自反性.对称性.传递性. 如果a和b存在等价关系,记 ...
- 并查集(Union/Find)模板及详解
概念: 并查集是一种非常精巧而实用的数据结构,它主要用于处理一些不相交集合的合并问题.一些常见的用途有求连通子图.求最小生成树的Kruskal 算法和求最近公共祖先等. 操作: 并查集的基本操作有两个 ...
- 【基本数据结构】并查集-C++
并查集,在一些有N个元素的集合应用问题中,我们通常是在开始时让每个元素构成一个单元素的集合,然后按一定顺序将属于同一组的元素所在的集合合并,其间要反复查找一个元素在哪个集合中.这一类问题近几年来反复出 ...
- 数据结构(并查集||树链剖分):HEOI 2016 tree
[注意事项] 为了体现增强版,题目限制和数据范围有所增强: 时间限制:1.5s 内存限制:128MB 对于15% 的数据,1<=N,Q<=1000. 对于35% 的数据,1<=N,Q ...
- POJ 1611 The Suspects 并查集 Union Find
本题也是个标准的并查集题解. 操作完并查集之后,就是要找和0节点在同一个集合的元素有多少. 注意这个操作,须要先找到0的父母节点.然后查找有多少个节点的额父母节点和0的父母节点同样. 这个时候须要对每 ...
- 【算法与数据结构】并查集 Disjoint Set
并查集(Disjoint Set)用来判断已有的数据是否构成环. 在构造图的最小生成树(Minimum Spanning Tree)时,如果采用 Kruskal 算法,每次添加最短路径前,需要先用并查 ...
- Java 并查集Union Find
对于一组数据,主要支持两种动作: union isConnected public interface UF { int getSize(); boolean isConnected(int p,in ...
- 【简单数据结构】并查集--洛谷 P1111
题目背景 AA地区在地震过后,连接所有村庄的公路都造成了损坏而无法通车.政府派人修复这些公路. 题目描述 给出A地区的村庄数NN,和公路数MM,公路是双向的.并告诉你每条公路的连着哪两个村庄,并告诉你 ...
随机推荐
- ethcode
pragma solidity ^0.4.0;contract Ballot { struct Voter { uint weight; bool voted; uint8 vote; address ...
- 基于TCP/IP的程序设计
TCP特点 (1)面向连接的传输 (2)端到端的通信 (3)高可靠性,确保传输数据的正确性,不会出现丢失或者乱序 (4)全双工方式传输 (5)采用字节流方式,以字节为单位传输字节序列 (6)紧急数据传 ...
- 《深入理解mybatis原理》 Mybatis数据源与连接池
对于ORM框架而言,数据源的组织是一个非常重要的一部分,这直接影响到框架的性能问题.本文将通过对MyBatis框架的数据源结构进行详尽的分析,并且深入解析MyBatis的连接池. 本文首先会讲述MyB ...
- 网络编程进阶---->>> hamc模块 socketserver模块验证合法性 两者进行通信连接
我们在工作中经常遇到,你公司内的某一台电脑要去访问你的服务器或者一个服务端电脑,那么你是让每一台都进行连接吗? 那不可能的 你肯定要进行限定的 验证客户端链接的合法性: hamc模块 hamc也是 ...
- [翻译] JSAnimatedImagesView
JSAnimatedImagesView 本人测试的效果: Description:描述 Easy to use UIView subclass to quickly add a cool anima ...
- CocoaPods -- ios项目中安装和使用CocoaPods
CocoaPods是什么? 当你开发iOS应用时,会经常使用到很多第三方开源类库,比如JSONKit,AFNetWorking等等.可能某个类库又用到其他类库,所以要使用它,必须得另外下载其他类库,而 ...
- 深入了解Node模块原理
深入了解Node模块原理 当我们编写JavaScript代码时,我们可以申明全局变量: var s = 'global'; 在浏览器中,大量使用全局变量可不好.如果你在a.js中使用了全局变量s,那么 ...
- [原]Ubuntu 下安装apache+PHP
1.安装apache2 sudo apt-get install apache2 运行如下命令重启:sudo /etc/init.d/apache2 restart 在浏览器里输入http://loc ...
- PyCharm创建虚拟环境 和 TVTK库的安装
注:示例系统环境:Windows10 64位 + Anaconda3: 昨天安装TVTK库遇到些麻烦,当时随便将库下到个文件夹安装后(没找到VENV文件夹...), 进行测试:from tvtk.to ...
- BZOJ1049:[HAOI2006]数字序列(DP)
Description 现在我们有一个长度为n的整数序列A.但是它太不好看了,于是我们希望把它变成一个单调严格上升的序列. 但是不希望改变过多的数,也不希望改变的幅度太大. Input 第一行包含一个 ...