并查集优化

并查集及其优化

并查集可以动态地连通两个点,可以非常快速判断两个点是否连通。假设存在 n 个节点,我们先将所有结点的 leader 标为自身;每次连接节点 i 和 j 时,我们可以将 i 的 leader 标记为 j ;每次要查询两个节点是否相连时,我们可以查找 i 和 j 的祖先是否最终为同一个。
并查集多用于无向图的关系判断。例如冗余连接中,需要删除一条边,来将有环无向图转换为无环无向图。循环遍历每条边,如果两个节点有共同的祖先,则删除该边即可。

简单并查集

public class UnionFind {
    int[] leader;
    int count;

    public UnionFind() {}
    public UnionFind(int size) {
        this.leader = new int[size];
        this.count = n;
        for (int i = 1; i < size; ++i) {
            leader[i] = i;
        }
    }

    public int getCount() {
        return this.count;
    }

    public int find(int idx) {
        while (idx != leader[idx]) {
            idx = leader[idx];
        }
        return idx;
    }

    public void union(int idx1, int idx2) {
        int ld1 = find(idx1), ld2 = find(idx2);
        if (ld1 == ld2) return;
        leader[ld1] = find(ld2);
        --count;
    }
}

对简单并查集进行优化

由于并查集是一颗树,查找效率和树高有关,尽量要保证树的高度低。
对简单并查集优化主要有两个方向:路径压缩、按秩合并。

路径压缩

理想情况:父节点下面直接连接着其子节点,且所有子节点都是叶子节点。
迭代方式:没有对中间节点的做leader节点做更新。
递归方式:修改了中间所有节点的leader节点。

public class UnionFind {
    //省略
    public int find1(int idx) {
        while (idx != leader[idx]) {
            idx = leader[idx];
        }
        return idx;
    }

    public int find2(int idx) {
        if (idx != leader[idx]) {
            leader[idx] = find(leader[idx]);
        }
        return leader[idx];
    }
}

按秩合并

秩:以当前节点为根的树的高度上限。秩初始化为0,合并两颗树时,对比他们的秩,秩较大的树为新的根节点,新父节点秩 += 1;
在union时,将秩小的树合并到秩大的树上。
只有根节点的秩有意义,非根节点的秩没有意义。
虽然秩由高度决定,但是我们不直接记录高度,因为树的高度在查找过程中可能会变。但秩代表的是上限,是不会变的,所以记录秩是更高效的做法。

public class UnionFind {
    public int[] leader;
    public int[] rank;
    public int count;

    public UnionFind() {}
    public UnionFind(int size) {
        this.count = size;
        this.leader = new int[size];
        this.rank = new int[size];
        for (int i = 1; i < size; ++i) {
            leader[i] = i;
        }
    }

    //省略

    public void union(int idx1, int idx2) {
        int ld1 = find(idx1), ld2 = find(idx2);
        if (ld1 == ld2) return;

        if (rank[ld1] < rank[ld2]) {
            leader[ld1] = ld2;
        } else if (rank[ld1] > rank[ld2] {
            leader[ld2] = ld1;
        } else {
            leader[ld1] = ld2;
            rank[ld2] += 1;
        }
        --count;
    }
}

最终优化版

public class UnionFind {
    public int[] leader;
    public int[] rank;
    public int count;

    public UnionFind(int size) {
        this.leader = new int[size];
        this.rank = new int[size];
        this.count = size;

        for (int i = 1; i < size; ++i) {
            leader[i] = i;
        }
    }

    public int getCount() {
        return this.count;
    }

    public int find(int idx) {
        if (idx != leader[idx]) {
            leader[idx] = find(leader[idx]);
        }
        return leader[idx];
    }

    public void union(int idx1, int idx2) {
        int ld1 = find(idx1), ld2 = find(idx2);
        if (ld1 == ld2) return;

        if (rank[ld1] < rank[ld2]) {
            leader[ld1] = ld2;
        } else if (rank[ld1] > rank[ld2]) {
            leader[ld2] = ld1;
        } else {
            leader[ld1] = ld2;
            ++rank[ld2];
        }
        --count;
    }
}

参考

Original: https://www.cnblogs.com/ogleede/p/16704727.html
Author: 茶倌
Title: 并查集优化

原创文章受到原创版权保护。转载请注明出处:https://www.johngo689.com/586353/

转载文章受原作者版权保护。转载请注明原作者出处!

(0)

大家都在看

  • Tinker Flutter热修复

    在Android里面,Flutter打包之后的产物是一个.so文件(libapp.so),Tinker热更新支持so文件更新,自然也就支持Flutter热更新。 理论上这样就可行了…

    Java 2023年5月30日
    0100
  • 2021/2/1

    系列文章目录 提示:这里可以添加系列文章的所有文章的目录,目录需要自己手动添加例如:第一章 Python 机器学习入门之pandas的使用 提示:写完文章后,目录可以自动生成,如何…

    Java 2023年6月5日
    069
  • 【转载】springboot四 全局异常处理

    http://tengj.top/2018/05/16/springboot13/ https://www.jb51.net/article/110533.htm Original…

    Java 2023年5月29日
    093
  • Nginx 同一个服务器设置二级域名

    设置二级域名 首先到域名运营商处设置二级域名使其生效。(已阿里的域名管理为例) 进入解析 添加记录,记录类型为 A;主机记录填写二级域名比如 picture,这样我的二级域名就是 …

    Java 2023年5月30日
    095
  • 聊聊消息队列高性能的秘密——零拷贝技术

    前言 RocketMQ为什么这么快、Kafka为什么这么快?用了零拷贝技术?什么是零拷贝技术,它们二者的零拷贝技术有不同吗? 为什么需要零拷贝 在计算机产业中,I/O的速度相较CP…

    Java 2023年6月6日
    0111
  • 如何在Docker容器中使用Arthas

    Arthas(阿尔萨斯) 能为你做什么? Arthas 是Alibaba开源的Java诊断工具,深受开发者喜爱。当你遇到以下类似问题而束手无策时, Arthas可以帮助你解决: 这…

    Java 2023年6月9日
    094
  • java package(包)的用法

    一般来说都用eclipse自动化图形工具搞定,我用的是ubuntu,所以需要自己打包引入。 什么是包? 这是对java源代码的组织和管理的一种方式,比如:当操作系统某个目录的文件非…

    Java 2023年5月29日
    092
  • Go语言中的函数及import导包

    一、函数的写法 1.基本写法: 类似:func 函数名 (a 数据类型, b 数据类型) 返回值类型{ //……. return c 2.多返回值,匿名:多…

    Java 2023年6月13日
    091
  • Ubuntu 20.04 开启局域网唤醒(WoL)

    打开主板相关设置 创建 systemd 自启动设置文件 vim /etc/systemd/system/wol@.service 放入以下内容: [Unit] Descriptio…

    Java 2023年6月7日
    087
  • JAVAEE学习路线分享

    今天把我的教学经验分享给大家。适合大多数人的学习路线。注:目前作者已经转行做java培训。 首先是培养兴趣。先开始学习HTML知识。也就是做网页,从这里开始比较简单,就是几个标签单…

    Java 2023年6月9日
    098
  • 兜兜转转,重回博客园。

    404. 抱歉,您访问的资源不存在。 可能是网址有误,或者对应的内容被删除,或者处于私有状态。 代码改变世界,联系邮箱 contact@cnblogs.com 园子的商业化努力-困…

    Java 2023年6月5日
    085
  • java 创建对象的几种方式

    1.用new语句创建对象 2.运用反射手段 3.调用对象的clone()方法 4.运用反序列化手段 代码 //new Student student = new Student()…

    Java 2023年5月29日
    073
  • SpringMVC(2)-注解开发MVC

    项目目录 一.创建maven项目,添加web支持,在pom.xml问价引入一下代码 <build> <resources> <resource>…

    Java 2023年6月9日
    077
  • 新写了一个Java并发程序设计教程

    新写了一个Java并发程序设计教程, 用于公司内部培训的,和2007年写的那个相比,内容更翔实一些。 内容列表 1、使用线程的经验:设置名称、响应中断、使用ThreadLocal …

    Java 2023年5月29日
    081
  • Spring Boot实现数据访问计数器

    1、数据访问计数器 在Spring Boot项目中,有时需要数据访问计数器。大致有下列三种情形: 1)纯计数:如登录的密码错误计数,超过门限N次,则表示计数器满,此时可进行下一步处…

    Java 2023年6月14日
    072
  • 【Android】线程池原理及Java简单实现

    线程池简介 多线程技术主要解决处理器单元内多个线程执行的问题,它可以显著减少处理器单元的闲置时间,增加处理器单元的吞吐能力。 假设一个服务器完成一项任务所需时间为: T1 创建线程…

    Java 2023年5月29日
    073
亲爱的 Coder【最近整理,可免费获取】👉 最新必读书单  | 👏 面试题下载  | 🌎 免费的AI知识星球