首页 > 其他分享 >并查集学习笔记

并查集学习笔记

时间:2023-11-04 15:31:49浏览次数:36  
标签:val parent int 查集 笔记 public 学习 集合 find

概念及其介绍

并查集是一种树型的数据结构,用于处理一些不相交集合的合并及查询问题。

并查集的思想是用一个数组表示了整片森林(parent)


  • 树的根节点唯一标识了一个集合
  • 我们只要找到了某个元素的的树根
  • 就能确定它在哪个集合里。

并查集构造方法

public class Union {

    int n;

    int[] parent;

    // size[i] 表示以 i 为根,集合中元素的个数
    int[] size;

    // rank[i] 表示以 i 为根为集合,所表示的输的层数
    int[] rank;

    public Union(int n, int[] parent) {
        // 表示集合的元素个数
        this.n = n;
        // 初始化数组
        parent = new int[n];
        // 填充数组
        for (int i = 0; i < n; i++) {
            // 这里表示  元素值 i 归属的集合编号 是 i
            parent[i] = i;

            // 初始化 每一个集合中的元素个数都为1
            size[i] = 1;

            // 初始化 树的层数 是 1
            rank[i] = 1;
        }
    }

}
  • 有三个重要概念,分别是 归属集合、集合中元素个数和树的层数
  • 归属集合
  • parent 数组进行表示
  • 元素个数
  • size 数组进行表示
  • 树的层数
  • rank 数组进行表示
  • 图例1

并查集学习笔记_并查集

  • 图例2

并查集学习笔记_数组_02

  • 编号 0 的集合现在只有一个元素就是其本身,层数为 1
  • 编号 1 的集合现在有两个元素 {0, 1},层数为 2

并查集快速查找

  • 问题提出:如何查找元素所在的集合编号?
public int find(int val) {
        assert val >= 0 && val < n;
        // 查找 val 所属的集合编号
        return parent[val];
    }
  • 但是这样存在一个问题,如下图

并查集学习笔记_数组_03

  • 如上图示:
  • find(0) = 1 : 表示 0 属于 编号为 1 的集合
  • find(1) = 2 : 表示 1 属于 编号为 2 的集合
  • 然后所有的元素都属于编号为 4 的集合,元素个数为 5,层数为 5
  • 所以该种方法无法找到元素最终归属的集合

优化一

查找目标元素所对应的根集合编号

public int find(int val) {
        assert val >= 0 && val < n;
        while(val != parent[val]) {
            val = parent[val];
        }
        return val;
    }
  • 以下图为例
  • val = 0 , parent[val] = 1,不相等-令 val = 1
  • val = 1 , parent[val] = 2->val = 2
  • val = 2 , parent[val] = 3->val = 3
  • val = 3 , parent[val] = 4->val = 4
  • val = 4 , parent[val] = 4最终返回 4 :元素 0 所属的集合编号是 4

优化二

优化一中的例子,需要计算 4 次之后才能够找到元素最终归属的集合编号

  • 但是实际上,{0, 1, 2, 3, 4} 都同属于 4号集合
  • 如下图,find(0) = 4 可以直接得出结果
  • 并且对应 层数 发生了变化 从 5 -> 2
  • 期望实现达到的效果是 如下图---> 从左到右 的转换
  • 代码实现如下
public int find(int val) {
        assert val >= 0 && val < n;
        while(val != parent[val]) {
            // 查找过程中不断更新
            parent[val] = find(parent[val]);
        }
        return val;
    }

并查集判断两个元素是否相连

public boolean isConnected(int a, int b) {
        return find(a) == find(b);
    }
  • 判断两个元素是否相连的方法较为简单
  • 直接利用find方法即可

并查集合并

并查集合并的相关代码和find方法的相关代码相关

路径优化

package new_start.classic;

/**
 * desc :  并查集--rank优化
 * <p>
 * create time : 2023/10/14 21:26
 */
public class UnionRankBest {

    int n;

    int[] parent;

    // 记录每一个集合的层数
    int[] rank;

    public UnionRankBest(int n) {
        this.n = n;
        parent = new int[n];
        rank = new int[n];
        for (int i = 0; i < n; i++) {
            parent[i] = i;
            // 初始化 rank[i] = 1
            rank[i] = 1;
        }
    }

    public int find(int val) {
        assert val >= 0 && val < n;
        while (val != parent[val]) {
            val = parent[val];
        }
        return val;
    }

    public boolean isConnected(int a, int b) {
        return find(a) == find(b);
    }

    public void union(int a, int b) {
        // 拿到 a b 所属的集合编号
        int rA = find(a);
        int rB = find(b);

        if (rA == rB) {
            // 同属于一个集合
            return;
        }

        if (rank[rA] < rank[rB]) {
            // a 的层数低
            parent[rA] = rB;
        } else if (rank[rA] > rank[rB]) {
            // 层数低 的 指向层数 高的,rank 不变
            parent[rB] = rA;
        } else {
            // rank[rA] == rank[rB]
            parent[rA] = rB;
            rank[rB] += 1;
        }
    }

}

size优化

package new_start.classic;

/**
 * desc :  并查集 size 优化
 * <p>
 * create time : 2023/10/14 21:39
 */
public class UnionSizeBest {

    int n;
    int[] parent;
    int[] size;

    public UnionSizeBest(int n) {
        this.n = n;
        parent = new int[n];
        size = new int[n];
        for (int i = 0; i < n; i++) {
            parent[i] = i;
            size[i] = 1;
        }
    }

    public int find(int v) {
        assert v >= 0 && v < n;
        while (v != parent[v]) {
            v = parent[v];
        }
        return v;
    }

    public boolean isConnected(int a, int b) {
        return find(a) == find(b);
    }

    public void union(int a, int b) {
        int rA = find(a);
        int rB = find(b);
        if (rA == rB) {
            return;
        }

        if (size[rA] < size[rB]) {
            // 如果 a 所属集合的元素比 b 少
            // 那么 将 元素少的集合 指向元素多的集合进行合并
            parent[rA] = rB;
            size[rB] += size[rA];
        } else {
            // a 集合的元素多
            parent[rB] = rA;
            size[rA] += size[rB];
        }
    }

}

路径压缩

package new_start.classic;

/**
 * desc :  并查集--路径压缩
 * <p>
 * create time : 2023/10/14 21:26
 */
public class UnionRankCompress {

    int n;

    int[] parent;

    public UnionRankCompress(int n) {
        this.n = n;
        parent = new int[n];
        for (int i = 0; i < n; i++) {
            parent[i] = i;
        }
    }

    public int find(int val) {
        assert val >= 0 && val < n;
        while (val != parent[val]) {
            // 查找并更新,减少 树的层数
            parent[val] = find(parent[val]);
        }
        return val;
    }

    public boolean isConnected(int a, int b) {
        return find(a) == find(b);
    }

    public void union(int a, int b) {
        // 拿到 a b 所属的集合编号
        int rA = find(a);
        int rB = find(b);

        if (rA == rB) {
            // 同属于一个集合
            return;
        }
        
        // a 所属集合 指向 b 所属集合,这个时候 经过 find 方法,层数为 2
        parent[rA] = rB;
    }

}

标签:val,parent,int,查集,笔记,public,学习,集合,find
From: https://blog.51cto.com/u_16079703/8183334

相关文章

  • 机器学习——卷积神经网络
     对于表格数据(其中行对应样本,列对应特征),我们寻找的模式可能涉及特征之间的交互,但是我们不能预先假设任何与特征交互相关的先验结构。此时,多层感知机可能是最好的选择,然而对于高维感知数据,这种缺少结构的网络可能会变得不实用。原因如下:当特征数非常高维时,全连接网络的......
  • 【刷题笔记】100. Same Tree
    题目Giventwobinarytrees,writeafunctiontocheckiftheyarethesameornot.Twobinarytreesareconsideredthesameiftheyarestructurallyidenticalandthenodeshavethesamevalue.Example1:Input:11/\/\......
  • 什么是机器学习中的正则化?
    1.引言在机器学习领域中,相关模型可能会在训练过程中变得过拟合和欠拟合。为了防止这种情况的发生,我们在机器学习中使用正则化操作来适当地让模型拟合在我们的测试集上。一般来说,正则化操作通过降低过拟合和欠拟合的可能性来帮助大家获得最佳模型。在本文中,我们将了解什么是正则化,......
  • STM32 PWM控制LED流水灯 学习记录随笔
    代码部分#include"stm32f10x.h"                 //Deviceheader#include"Delay.h"intmain(void){   RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);//启用系统寄存器时钟,使能GPIOC组,并启动   GPIO_InitTypeDefGPIO_InitStructure;  ......
  • 20211105李宜时信息安全系统设计与基础学习笔记八
    Ubuntu中的定时器及时钟服务学习笔记基础概念在Ubuntu系统中,定时器和时钟服务是操作系统时间管理的基础。定时器用于在特定时间点或经过特定时间间隔后触发事件。时钟服务则提供当前时间和日期信息。硬件定时器硬件定时器是由计算机硬件提供的计时设备,它可以在不同时间间隔发......
  • React学习笔记18-非受控组件
    1.非受控组件的定义非受控组件即状态不是完全由React的state来控制的组件React要编写一个非受控组件,可以使用ref来从DOM节点中获取表单数据,就是非受控组件。importReact,{Component}from'react'exportdefaultclassAppextendsComponent{myusername=R......
  • React学习笔记19-受控组件
    1.受控组件的定义React组件的数据渲染是否被调用者传递的props完全控制,完全控制则为受控组件,否则非受控组件。即React的state成为组件的唯一数据源。 下面用一个小案例来演示,案例中todolist组件的唯一数据源就是State,todolist组件就是一个受控组件importReact,{Com......
  • linux系统信息命令笔记
     1,时间和日期  2,磁盘信息  4,进程概念介绍4.1,ps基本命令使用 psaux显示内容太多了。一般用psa或psau 4.2,top命令的基本使用top可以动态的显示运行中的进程并排序,退出top,输出q 4.3,kill命令的基本使用PID是进程代号。kill指定代号:终止......
  • 《软件工程导论》读书笔记一
    《软件工程导论》是一本非常全面且深入的书籍,涵盖了许多关键的主题,包括需求分析、系统设计、项目管理、质量保证以及更多其他主题。软件工程的重要性:理解为什么我们需要软件工程,它对现代社会的影响以及它的必要性。软件开发生命周期(SDLC):介绍软件开发过程的主要阶段,并详细讨论每......
  • vue学习十一
    <divid="app11"><h3>哞哞俩数计算器</h3><inputtype="number"v-model="num1"><selectv-model="chart"><optionvalue="+">+</option>......