Java 并查集Union Find
对于一组数据,主要支持两种动作:
union
isConnected
- public interface UF {
- int getSize();
- boolean isConnected(int p,int q);
- void unionElements(int p,int q);
- }
- public class UnionFind1 implements UF{
- private int[] id;
- public UnionFind1(int size){
- id=new int[size];
- for (int i = 0; i < id.length; i++) {
- id[i]=i;
- }
- }
- @Override
- public int getSize(){
- return id.length;
- }
- //查找元素p所对应的集合编号
- private int find(int p) {
- if(p<0&&p>id.length)
- throw new IllegalArgumentException("p is out of bound.");
- return id[p];
- }
- //查看元素p和元素q是否所属一个集合
- @Override
- public boolean isConnected(int p,int q){
- return find(p)==find(q);
- }
- //合并元素p和元素q所属的集合
- @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;
- }
- }
- }
由子数指向父的并差集
- public class UnionFind2 implements UF {
- private int[] parent;
- public UnionFind2(int size){
- parent =new int[size];
- for(int i=0;i<size;i++)
- parent[i]=i;
- }
- @Override
- public int getSize(){
- return parent.length;
- }
- //查找过程,查找元素p所对应的集合编号
- private int find(int p){
- if(p<0&&p>=parent.length)
- throw new IllegalArgumentException("p is out of bound.");
- while (p!=parent[p])
- p=parent[p];
- return p;
- }
- //查找元素p和元素q是否所属一个集合
- @Override
- public boolean isConnected(int p,int q){
- return find(p)==find(q);
- }
- //合并元素p和元素q所属的集合
- @Override
- public void unionElements(int p,int q){
- int pRoot =find(p);
- int qRoot=find(q);
- if(pRoot==qRoot)
- return;
- parent[pRoot]=qRoot;
- }
- }
测试:
- 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){
- int size=10000;
- int m=10000;
- UnionFind1 uf1=new UnionFind1(size);
- System.out.println("UnionFind1:"+testUF(uf1, m)+"s");
- UnionFind2 uf2=new UnionFind2(size);
- System.out.println("UnionFind1:"+testUF(uf2, m)+"s");
- }
- }
第三种:(size)
- 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];
- }
- }
第四种:(rank)
- public class UnionFind4 implements UF {
- private int[] rank; // rank[i]表示以i为根的集合所表示的树的层数
- private int[] parent; // parent[i]表示第i个元素所指向的父节点
- // 构造函数
- public UnionFind4(int size){
- rank = new int[size];
- parent = new int[size];
- // 初始化, 每一个parent[i]指向自己, 表示每一个元素自己自成一个集合
- for( int i = 0 ; i < size ; i ++ ){
- parent[i] = i;
- rank[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;
- // 根据两个元素所在树的rank不同判断合并方向
- // 将rank低的集合合并到rank高的集合上
- if(rank[pRoot] < rank[qRoot])
- parent[pRoot] = qRoot;
- else if(rank[qRoot] < rank[pRoot])
- parent[qRoot] = pRoot;
- else{ // rank[pRoot] == rank[qRoot]
- parent[pRoot] = qRoot;
- rank[qRoot] += 1; // 此时, 我维护rank的值
- }
- }
- }
第五种:(路径压缩)
- public class UnionFind5 implements UF {
- // rank[i]表示以i为根的集合所表示的树的层数
- // 在后续的代码中, 我们并不会维护rank的语意, 也就是rank的值在路径压缩的过程中, 有可能不在是树的层数值
- // 这也是我们的rank不叫height或者depth的原因, 他只是作为比较的一个标准
- private int[] rank;
- private int[] parent; // parent[i]表示第i个元素所指向的父节点
- // 构造函数
- public UnionFind5(int size){
- rank = new int[size];
- parent = new int[size];
- // 初始化, 每一个parent[i]指向自己, 表示每一个元素自己自成一个集合
- for( int i = 0 ; i < size ; i ++ ){
- parent[i] = i;
- rank[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.");
- while( p != parent[p] ){
- parent[p] = parent[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;
- // 根据两个元素所在树的rank不同判断合并方向
- // 将rank低的集合合并到rank高的集合上
- if( rank[pRoot] < rank[qRoot] )
- parent[pRoot] = qRoot;
- else if( rank[qRoot] < rank[pRoot])
- parent[qRoot] = pRoot;
- else{ // rank[pRoot] == rank[qRoot]
- parent[pRoot] = qRoot;
- rank[qRoot] += 1; // 此时, 我维护rank的值
- }
- }
- }
第六种:(递归)
- public class UnionFind6 implements UF {
- // rank[i]表示以i为根的集合所表示的树的层数
- // 在后续的代码中, 我们并不会维护rank的语意, 也就是rank的值在路径压缩的过程中, 有可能不在是树的层数值
- // 这也是我们的rank不叫height或者depth的原因, 他只是作为比较的一个标准
- private int[] rank;
- private int[] parent; // parent[i]表示第i个元素所指向的父节点
- // 构造函数
- public UnionFind6(int size){
- rank = new int[size];
- parent = new int[size];
- // 初始化, 每一个parent[i]指向自己, 表示每一个元素自己自成一个集合
- for( int i = 0 ; i < size ; i ++ ){
- parent[i] = i;
- rank[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.");
- // path compression 2, 递归算法
- if(p != parent[p])
- parent[p] = find(parent[p]);
- return parent[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;
- // 根据两个元素所在树的rank不同判断合并方向
- // 将rank低的集合合并到rank高的集合上
- if( rank[pRoot] < rank[qRoot] )
- parent[pRoot] = qRoot;
- else if( rank[qRoot] < rank[pRoot])
- parent[qRoot] = pRoot;
- else{ // rank[pRoot] == rank[qRoot]
- parent[pRoot] = qRoot;
- rank[qRoot] += 1; // 此时, 我维护rank的值
- }
- }
- }
Java 并查集Union Find的更多相关文章
- 并查集(Union/Find)模板及详解
概念: 并查集是一种非常精巧而实用的数据结构,它主要用于处理一些不相交集合的合并问题.一些常见的用途有求连通子图.求最小生成树的Kruskal 算法和求最近公共祖先等. 操作: 并查集的基本操作有两个 ...
- java 并查集
并查集代码 并查集优化⼀ 并查集优化⼆ 实战题⽬目1. https://leetcode.com/problems/number-of-islands/2. https://leetcode.com/ ...
- POJ 1611 The Suspects 并查集 Union Find
本题也是个标准的并查集题解. 操作完并查集之后,就是要找和0节点在同一个集合的元素有多少. 注意这个操作,须要先找到0的父母节点.然后查找有多少个节点的额父母节点和0的父母节点同样. 这个时候须要对每 ...
- java——并查集 UnionFind
时间复杂度: O(log*n),近乎是O(1)级别的 UnionFind 接口: public interface UF { int getSize(); boolean isConnected(in ...
- 最小生成树(Minimum Spanning Tree)——Prim算法与Kruskal算法+并查集
最小生成树——Minimum Spanning Tree,是图论中比较重要的模型,通常用于解决实际生活中的路径代价最小一类的问题.我们首先用通俗的语言解释它的定义: 对于有n个节点的有权无向连通图,寻 ...
- bzoj1854 [Scoi2010]游戏【构图 并查集】
传送门:http://www.lydsy.com/JudgeOnline/problem.php?id=1854 没想到怎么做真是不应该,看到每个武器都有两个属性,应该要想到连边构图的!太不应该了! ...
- [leetcode] 并查集(Ⅰ)
预备知识 并查集 (Union Set) 一种常见的应用是计算一个图中连通分量的个数.比如: a e / \ | b c f | | d g 上图的连通分量的个数为 2 . 并查集的主要思想是在每个连 ...
- 并查集(Java实现)
(最好在电脑下浏览本篇博客...手机上看代码不方便) 当时学的时候看的一本印度的数据结构书(好像是..有点忘了..反正跟同学们看的都不一样...)...里面把本文提到的所有情况都提到了,我这里只是重复 ...
- 并查集的Java实现
Java实现并查集,合并时采用路径压缩算法. 如果合并时使用循环修改的方法,一次合并的时间复杂度就为N,无法接受 public class Union { public int[] id;//对应索引 ...
随机推荐
- bzoj 5301: [Cqoi2018]异或序列 (莫队算法)
链接:https://www.lydsy.com/JudgeOnline/problem.php?id=5301 题面; 5301: [Cqoi2018]异或序列 Time Limit: 10 Sec ...
- Redisson分布式锁实现
转: Redisson分布式锁实现 2018年09月07日 15:30:32 校长我错了 阅读数:3303 转:分布式锁和Redisson实现 概述 分布式系统有一个著名的理论CAP,指在一个分布 ...
- 从redis中取值如果不存在设置值,使用Redisson分布式锁【我】
用到的jar包: <!-- Redis客户端 --> <dependency> <groupId>redis.clients</groupId> < ...
- spring的DI.IoC是什么
最近要搞spring的单元测试,不得已啊啊啊啊啊啊啊啊啊又要开始搞spring…… 日目晶…… 搞这几个概念,先甩一部分代码: UserDao 接口 package com.itheima.ioc; ...
- uboot中的中断macro宏
目录 uboot中的中断macro宏 引入 内存分配 流程概览 普通中断 保存现场 中断函数打印具体寄存器 恢复现场 软中断 空间获取 保存现场 附录速记 疑惑待解 title: uboot中的中断m ...
- 编写高质量的Python代码系列(二)之函数
Python中的函数具备多种特性,这可以简化编程工作.Python函数的某些性质与其他编程语言中的函数相似,但也有性质是Python独有的.本节将介绍如何用函数来表达亿图.提升可复用程度,并减少Bug ...
- Mathematica 代码
s1 = ContourPlot3D[x^2 + z^2 == 1, {x, -1, 1}, {y, 0, 1}, {z, -1, 1}] s2 = ContourPlot3D[ y == 1 + S ...
- python selenium 最简单示例
使用 pip 安装 selenium 下载 chromedriver,添加在PATH中 # -*- coding: utf-8 -*- from selenium import webdriver ...
- 剑指Offer-翻转单词顺序列
题目描述 牛客最近来了一个新员工Fish,每天早晨总是会拿着一本英文杂志,写些句子在本子上.同事Cat对Fish写的内容颇感兴趣,有一天他向Fish借来翻看,但却读不懂它的意思.例如,"st ...
- 通过配置文件新建solr的core
目录solr-7.5.0\server\solr 1. 新建文件夹 test-core 2. 在文件夹test-core下新建core.properties name=test-core confi ...