当前位置:

首页 > 编程开发 > java中并查集的示例分析

java中并查集的示例分析

一、概述并查集:一种树型数据结构,用于解决一些不相交集合的合并及查询问题。例如:有n个村庄,查询2个村庄之间是否有连接的路,连接2个村庄两大核心:查找(Find):查找元素所在的集合合并(Union):将两个元素所在集合合并为一个集合二、实现并查集有两种常见的实现思路快查(QuickFind)查找(Find)的时间复杂度:O(1)合并(Union)的时间复杂度:O(n)快并(QuickUnion)查找(Find)的时间复杂度:O(logn)可以优化至O(a(n))a(n)

本文目录

    一、概述

    并查集:一种树型数据结构,用于解决一些不相交集合的合并及查询问题。例如:有n个村庄,查询2个村庄之间是否有连接的路,连接2个村庄

    两大核心:

    查找 (Find) : 查找元素所在的集合

    合并 (Union) : 将两个元素所在集合合并为一个集合

    二、实现

    并查集有两种常见的实现思路

    快查(Quick Find)

    • 查找(Find)的时间复杂度:O(1)

    • 合并(Union)的时间复杂度:O(n)

    快并(Quick Union)

    • 查找(Find)的时间复杂度:O(logn)可以优化至O(a(n))a(n)< 5

    • 合并(Union)的时间复杂度:O(logn)可以优化至O(a(n))a(n)< 5

    使用数组实现树型结构,数组下标为元素,数组存储的值为父节点的值

    java中并查集的示例分析

    创建抽象类Union Find

    public abstract class UnionFind {
     
    	 int[] parents;
    	/**
    	 * 初始化并查集
    	 * @param capacity
    	 */
    	public UnionFind(int capacity){
    		
    		if(capacity < 0) {
    			throw new IllegalArgumentException("capacity must be >=0");
    		}
            //初始时每一个元素父节点(根结点)是自己
    		parents = new int[capacity];
    		for(int i = 0; i < parents.length;i++) {
    			parents[i] = i;
    		}
    	}
    	
       /**
         *  检查v1 v2 是否属于同一个集合
         */
    	public boolean isSame(int v1,int v2) {
    		return find(v1) == find(v2);
    	}
     
        /**
         *  查找v所属的集合 (根节点)
         */
    	public  abstract int find(int v);
     
    	/**
         *  合并v1 v2 所属的集合
         */
    	public abstract void union(int v1, int v2);
    	
    		
    	// 范围检查
    	public   void rangeCheck(int v)  {
    		if(v<0 || v > parents.length)
    			throw new IllegalArgumentException("v is out of capacity");
    	}
    }

    2.1 Quick Find实现

    以Quick Find实现的并查集,树的高度最高为2,每个节点的父节点就是根节点

    java中并查集的示例分析

    public class UnionFind_QF extends UnionFind {
    	public UnionFind_QF(int capacity) {
    		super(capacity);
    		
    	}
     
      // 查
    @Override
    	public  int  find(int v) {
    		rangeCheck(v);
    		return parents[v];
    	}
     
     // 并 将v1所在集合并到v2所在集合上
    @Override
    public void union(int v1, int v2) {
        // 查找v1 v2 的父(根)节点
    	int p1= find(v1);
    	int p2 = find(v2);
    	if(p1 == p2) return;
      
        //将所有以v1的根节点为根节点的元素全部并到v2所在集合上 即父节点改为v2的父节点
    	for(int i = 0; i< parents.length; i++) {
    		if(parents[i] == p1) {
    			parents[i] = p2;
    		}
    	}
    	
      }
    }

    2.2 Quick Union实现

    java中并查集的示例分析

    public class UnionFind_QU extends UnionFind {
     
    	public UnionFind_QU(int capacity) {
    		super(capacity);
    		
    	}
     
    	//查某一个元素的根节点
    	@Override
    	public int find(int v) {
       //检查下标是否越界
    		rangeCheck(v);
       
      // 一直循环查找节点的根节点
    		while (v != parents[v]) {
    			v = parents[v];
    		}
    		return v;
    	}
     
    //V1 并到 v2 中
    	@Override
    	public void union(int v1, int v2) {
    	
    		int p1 = find(v1);
    		int p2 = find(v2);
    		if(p1 == p2) return;
          //将v1 根节点 的 父节点 修改为 v2的根结点 完成合并
    		parents[p1] = p2;
    	}
    }

    三、优化

    并查集常用快并来实现,但是快并有时会出现树不平衡的情况

    java中并查集的示例分析

    有两种优化思路:rank优化,size优化 

    3.1基于size的优化

    核心思想:元素少的树 嫁接到 元素多的树

    public class UniondFind_QU_S extends UnionFind{
     
       // 创建sizes 数组记录 以元素(下标)为根结点的元素(节点)个数
    	private int[] sizes;
     
    	public UniondFind_QU_S(int capacity) {
    		super(capacity);
     
    		sizes = new int[capacity];
     
       //初始都为 1
    		for(int i = 0;i < sizes.length;i++) {
    			sizes[i] = 1;
    			}
    		
    	}
     
    	@Override
    	public int find(int v) {
     
    		rangeCheck(v);
     
    		while (v != parents[v]) {
    			v = parents[v];
    		}
    		return v;
    	}
     
    	@Override
    	public void union(int v1, int v2) {
    	
    		int p1 = find(v1);
    		int p2 = find(v2);
    		if(p1 == p2) return;
     
    		//如果以p1为根结点的元素个数 小于 以p2为根结点的元素个数 p1并到p2上,并且更新p2为根结点的元素个数
    	if(sizes[p1] < sizes[p2]) {
    		    parents[p1] = p2;
    		    sizes[p2] += sizes[p1];
    		
     // 反之 则p2 并到 p1 上,更新p1为根结点的元素个数
    	}else {
    			parents[p2] = p1;
    			sizes[p1] += sizes[p2];
    		}
    	}
    }

    基于size优化还有可能会导致树不平衡

    3.2基于rank优化

    核心思想:矮的树 嫁接到 高的树

    public class UnionFind_QU_R extends UnionFind_QU {
       // 创建rank数组  ranks[i] 代表以i为根节点的树的高度
     private int[] ranks;
     
    	public UnionFind_QU_R(int capacity) {
    		super(capacity);
     
    		ranks = new int[capacity];
     
    		for(int i = 0;i < ranks.length;i++) {
    			ranks[i] = 1;
    		}
     
    	}
        
    	public void union(int v1, int v2) {
     
    		int p1 = find(v1);
    		int p2 = find(v2);
    		if(p1 == p2) return;
        
        // p1 并到 p2 上 p2为根 树的高度不变
    		if(ranks[p1] < ranks[p2]) {
    			parents[p1] = p2;
    			
      // p2 并到 p1 上 p1为根 树的高度不变
    		} else if(ranks[p1] > ranks[p2]) {
    			parents[p2] = p1;
     
    		}else {
        // 高度相同 p1 并到 p2上,p2为根 树的高度+1
    			parents[p1] = p2;
    			ranks[p2] += 1;
    		}
    	}
    }

    基于rank优化,随着Union次数的增多,树的高度依然会越来越高  导致find操作变慢

    有三种思路可以继续优化 :路径压缩、路径分裂、路径减半

    3.2.1路径压缩(Path Compression )

    在find时使路径上的所有节点都指向根节点,从而降低树的高度

    java中并查集的示例分析

    /**
     *  Quick Union -基于rank的优化  -路径压缩
     *
     */
    public class UnionFind_QU_R_PC extends UnionFind_QU_R {
     
    	public UnionFind_QU_R_PC(int capacity) {
    		super(capacity);
    		
    	}
     
    	@Override
    	public int find(int v) {
    		rangeCheck(v);
     
    		if(parents[v] != v) {
     
            //递归 使得从当前v 到根节点 之间的 所有节点的 父节点都改为根节点
    			parents[v] = find(parents[v]);
    		}
    		return parents[v];
    	}
    }

    虽然能降低树的高度,但是实现成本稍高 

    3.2.2路径分裂(Path Spliting)

    使路径上的每个节点都指向其祖父节点

    java中并查集的示例分析

    /**
     *  Quick Union -基于rank的优化  -路径分裂
     *
     */
    public class UnionFind_QU_R_PS extends UnionFind_QU_R {
     
    	public UnionFind_QU_R_PS(int capacity) {
    		super(capacity);
    		
    	}
     
    	@Override
    	public int find(int v) {
    		rangeCheck(v);
    		while(v != parents[v]) {
     
    			int p = parents[v];
    			parents[v] = parents[parents[v]];
    			v = p;
    		}
    		return v;
    	}
    }
    3.2.3路径减半(Path Halving)

    使路径上每隔一个节点就指向其祖父节点

    java中并查集的示例分析

    /**
     *  Quick Union -基于rank的优化  -路径减半
     *
     */
    public class UnionFind_QU_R_PH extends UnionFind_QU_R {
     
    	public UnionFind_QU_R_PH(int capacity) {
    		super(capacity);
    		
    	}
    	
    	
        public int find(int v) {
        	rangeCheck(v);
     
    		while(v != parents[v]) {
    			parents[v] = parents[parents[v]];
    			v = parents[v];
    		}
    		return v;
    	}	 
     }

    使用Quick Union + 基于rank的优化 + 路径分裂 或 路径减半

    可以保证每个操作的均摊时间复杂度为O(a(n)) , a(n) < 5

    本站声明:本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系bd@zhengruan.com
    作者最新文章
    编程开发 Java
    相关文章 更多
    Java单元测试入门:使用JUnit 5编写第一个测试用例
    Java单元测试入门:使用JUnit 5编写第一个测试用例

    面向初学者的JUnit 5实用入门指南。从测试概念、Maven依赖配置,到手把手编写首个单元测试用例及断言验证,助你掌握Java单元测试的核心流程。

    Java变量未初始化报错解析:局部变量与成员变量默认值差异
    Java变量未初始化报错解析:局部变量与成员变量默认值差异

    深入解析Java中局部变量未初始化编译报错与成员变量自动赋予默认值的机制差异,涵盖JVM内存分配、编译期确定性赋值检查与实际排查修复方案。

    Java获取当前时间并格式化输出:DateTimeFormatter与SimpleDateFormat
    Java获取当前时间并格式化输出:DateTimeFormatter与SimpleDateFormat

    详细讲解在Java中获取当前系统时间并进行格式化输出的两种核心方案:基于Java 8全新日期时间API的DateTimeFormatter与传统遗留的SimpleDateFormat,包含完整代码示例、线程安全机制对比及生产选型建议。

    Java中final与static final常量的定义及初始化规则
    Java中final与static final常量的定义及初始化规则

    深入解析Java中final变量与static final常量的定义、内存语义、初始化时机及合法赋值路径,涵盖编译期常量优化与实战避坑指南。

    Java抽象方法与接口方法的区别及选型建议
    Java抽象方法与接口方法的区别及选型建议

    深入对比Java中抽象类抽象方法与接口方法的语法演进、设计意图及运行期差异,并提供清晰的架构选型决策流程与实战范例。

    Java实现HTTP请求与JSON解析的完整代码示例
    Java实现HTTP请求与JSON解析的完整代码示例

    本文提供Java标准库HttpClient发起HTTP GET/POST请求并结合Jackson进行JSON数据反序列化与序列化的完整代码示例与工程实践指南。

    Java递归出现StackOverflowError的原因分析及栈空间优化方案
    Java递归出现StackOverflowError的原因分析及栈空间优化方案

    详细分析Java递归调用出现StackOverflowError的原因,结合代码说明线程栈、栈帧和递归深度之间的关系,并介绍终止条件排查、迭代改写、减少栈帧开销以及通过-Xss调整JVM栈空间的方法。

    Java空指针异常(NPE)的代码排查与修复实例
    Java空指针异常(NPE)的代码排查与修复实例

    针对Java中常见的NullPointerException(NPE),本文从堆栈日志定位、典型代码触发场景、具体修复代码对比到防御体系进行系统讲解,提供实用且易上手的排查与修复方案。

    selenium上传文件教程本地文件路径设置方法
    selenium上传文件教程本地文件路径设置方法

    深入解析Selenium处理文件上传的技术细节,涵盖标准HTML控件的路径直接注入法与非标准控件的系统级模拟方案,提供Windows与Linux环境下的路径设置最佳实践及常见异常排查。

    大数据开发语言选型:Java对比Scala与Python的优缺点及适用场景
    大数据开发语言选型:Java对比Scala与Python的优缺点及适用场景

    针对大数据工程场景,系统对比Java、Scala与Python在执行性能、生态结合、开发效率和维护成本上的核心差异,给出清晰的架构选型依据与落地方案。

    查看更多
    精品专题 更多
    装机必备
    装机必备

    正软商城装机必备专区,精选办公、浏览器、安全防护、影音播放、压缩解压、设计创作和系统工具等电脑常用正版软件,帮助用户快速完成新电脑软件配置。

    Windows
    Windows

    正软商城Windows软件专区,汇集适用于Windows电脑的办公、设计、安全防护、影音播放、开发工具和系统优化软件,提供软件介绍、系统要求、正版授权及购买下载服务。

    PDF教程
    PDF教程

    正软商城PDF教程频道提供PDF编辑、转换、合并、拆分、压缩及格式处理方法,同时介绍常用PDF软件和工具的使用技巧。

    Mac软件 更多
    讯飞智作 网页版
    讯飞智作 网页版
    Windows/ macOS/ Linux

    讯飞智作是一款基于语音合成技术、虚拟人驱动技术与数字人形象声音构建能力的 AI 数字人音视频创作平台。

    火山引擎 网页版
    火山引擎 网页版
    Windows/ macOS/ Linux

    火山引擎是字节跳动旗下企业级云与AI服务平台。聚焦大模型核心能力,依托字节自研豆包多模态大模型,凭借火山方舟一站式平台,提供模型精调、推理部署、智能应用搭建等全链路服务,结合高性能AI算力,助力各行业快速落地大模型数字化与智能化应用。

    Aperty macOS版
    Aperty macOS版
    macOS

    Aperty 是 Skylum 推出的 AI 人像修图软件,支持自然肤质修饰、瑕疵处理、面部与身体调整、预设及批量同步。

    WINDOWS 更多
    讯飞智作 网页版
    讯飞智作 网页版
    Windows/ macOS/ Linux

    讯飞智作是一款基于语音合成技术、虚拟人驱动技术与数字人形象声音构建能力的 AI 数字人音视频创作平台。

    火山引擎 网页版
    火山引擎 网页版
    Windows/ macOS/ Linux

    火山引擎是字节跳动旗下企业级云与AI服务平台。聚焦大模型核心能力,依托字节自研豆包多模态大模型,凭借火山方舟一站式平台,提供模型精调、推理部署、智能应用搭建等全链路服务,结合高性能AI算力,助力各行业快速落地大模型数字化与智能化应用。

    PixTV 网页版
    PixTV 网页版
    Windows

    PixTV 是一款在浏览器中使用的 AI 视觉内容创作平台,提供图片、视频、音频、文字生成、AI 分镜、局部重绘、提示词反推和无限画布等工具,适合短剧、漫剧、广告及社交媒体内容制作。