算法学习-并查集(持续更新中)

news2024/11/16 9:39:37

本文参考:

最容易理解的并查集详解
详解:并查集(Union-Find)
「代码随想录」684. 冗余连接:【并查集基础题目】详解!
并查集从入门到出门

并查集常常在做图相关的题目时冒出来,但是笔者经常去回避这样的解法,这次找到个机会讲相关的知识和题目进行汇总。

基础知识

并查集常常用来解决图的连通性相关的问题,主要实现了下面几个方法:

class UnionFind {
	private int count; //记录连通分量
    private int[] parent; //节点x的父亲节点是parent[x]
    
    /* 将 p 和 q 连接 */
    public void union(int p, int q);
    /* 判断 p 和 q 是否连通 */
    public boolean connected(int p, int q);
    /* 返回当前节点的根节点 */
    public int find(int x);
    /* 返回图中有多少个连通分量 */
    public int count();
}

其中最重要的是「并」和「查」:

  • union – 合并两个节点,把两个节点所在的连通分量合并成一个
  • find – 查找节点所属的连通分量(也就是根)

「集合」使用一个根节点来标识:

数组 parent[] 来表示每个节点的父亲节点,如果自己就是根节点,那么 parent[i] = i,即自己指向自己.

其中方法的具体实现如下:

class UnionFind{
    private int count; //记录连通分量
    private int[] parent; //节点x的父亲节点是parent[x]
    
    public UnionFind(int n){
    	// 一开始互不连通
        this.count = n;
        parent = new int[n];
        // 每个节点是独立的环,父亲节点就是自己
        for(int i = 0;i<n;i++){
            parent[i] = i;
        }
    }
	
	// 将节点p和q连接, 如果两个节点被连通,那么则让其中的一个根节点连接到另一个节点的根节点上
    public void union(int a,int b){
        int parentA = find(a);
        int parentB = find(b);
        if(parentA == parentB) return;
        // 将两颗树合并为一颗
        parent[parentA] = parentB;
        // 连通分量-1
        count--;
    }
	
	// 并查集里寻根的过程,迭代做法,从父节点向上继续找
    public int find1(int x){
          //根节点的parent[x]==x
          while (parent[x]!=x){
              x=parent[x];
          }
          return x;
     }
	
	// 并查集里寻根的过程,递归做法
    public int find2(int u) {
        if (x==parent[x]) return x;
        else return find(parent[u])}
	
	// 判断p和q是否连通:如果两个节点是连通的,那么他们一定拥有相同的根节点
    public boolean connected(int a,int b){
        return find(a) == find(b);
    }
	
	// 返回当前连通分量的个数
    public int getCount(){
        return this.count;
    }
}

优化方法

平衡性优化

思路:当我们每次连接两个节点的时候,不希望出现头重脚轻的情况,而希望到达一种平衡的状态
在这里插入图片描述

使用额外的一个数组 size[] 记录每个连通分量中的节点数,每次均把节点数少的分量接到节点数多的分量上,如下图所示:

注意:只有每个连通分量的根节点的 size[] 才可以代表该连通分量中的节点数

private int count;
private int[] parent;
private int[] size;

// 构造函数
public UnionFind (int n) {
    this.count = n;
    parent = new int[n];
    size = new int[n];
    for (int i = 0; i < n; i++) {
        parent[i] = i;
        // 最初,每个连通分量均为 1
        size[i] = 1;
    }
}

public void union(int p, int q) {
    int rootP = find(p);
    int rootQ = find(q);
    if (rootP == rootQ) return;
    /******** 修改部分 ********/
    if (size[rootP] < size[rootQ]) {
        parent[rootP] = rootQ;
        size[rootQ] += size[rootP]
    } else {
        parent[rootQ] = rootP;
        size[rootP] += size[rootQ]
    }
    /********** end **********/
    count--;
}

路径压缩

分析上述实现的方法,find() 是决定并查集时间复杂度的重要因素。抛开 find() 因素,其他方法的时间复杂度均可视为 O ( 1 ) O(1) O(1)。所以如果要优化算法的时间复杂度,需要从 find() 入手。

对于有 n 个节点 1 个连通分量的并查集来说,最坏的时间复杂度为 O ( n ) O(n) O(n),最好的时间复杂度为 O ( 1 ) O(1) O(1)

  • 最坏情况:全部只有左子节点
  • 最好情况:n - 1 叉树,即根节点有 n - 1 个子节点

find()的过程中将树高进行压缩,即子节点尽可能一步指向根节点:

迭代做法,子节点指向父亲节点的父亲节点

public int find(int x) {
    while (parent[x] != x) {
        // 进行路径压缩
        parent[x] = parent[parent[x]];
        x = parent[x];
    }
    return x;

递归做法,由于find()递归返回的是一个根,所以能一次性将这条路径上的节点全部拉平。

private int find(int x) {
    if (parent[x] != x) {
        parent[x] = find(parent[x]);
    }
    return parent[x];
}

相关题目

参考题目,并查集从入门到出门

684.冗余连接

并查集,原来无向无环的图加入了一条边以后存在了一个环,现在要删除一条边让它继续无环,当有多种删除方案时,选择 edges 中最后出现的边。

聚焦于点,从前向后遍历每一条边,边的两个节点如果不在同一个集合,就将这条边的两个节点加入同一个集合,当遍历到某条边却发现其两个节点已经在同一个集合里,再加入这条边就会成环。

在进行路径压缩以后,find()可以在 O ( 1 ) O(1) O(1)内找到根节点,因此总体时间复杂度为 O ( N ) O(N) O(N)

class Solution {
    public int[] findRedundantConnection(int[][] edges) {
        UnionFind unionFind=new UnionFind(edges.length);
        for(int[]e:edges){
            if(unionFind.isConnect(e[0],e[1])){
                return e;
            }
            unionFind.union(e[0],e[1]);
        }
        
        return new int[0];
    }

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

        public void union(int a, int b){
            int parentA=find(a);
            int parentB=find(b);
            if(parentA==parentB) return;
            parent[parentA]=parentB;
            this.count--;
        }

        // public int find(int x){
        //     if(parent[x]==x) return parent[x];
        //     else return find(parent[x]);
        // }

        public int find(int x){
            if(parent[x]!=x){
                parent[x]=find(parent[x]);
            }
            return parent[x];
        }

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

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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/134321.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Exynos_4412——轮询与中断

目录 一、CPU与硬件的交互方式 1.1轮询 1.2中断 1.3DMA 二、轮询方式的按键实验 三、GPIO中断相关寄存器 四、GPIO中断编程 五、小作业 一、CPU与硬件的交互方式 1.1轮询 CPU执行程序时不断地询问硬件是否需要其服务&#xff0c;若需要则给予其服务&#xff0c;若不需…

Android Jetpack Compose——一个简单的微信界面

一个简单的微信界面简述效果视频底部导航栏导航元素导航栏放入插槽绘制地图消息列表效果图实现聊天效果图实现气泡背景联系人界面效果图实现好友详情效果图实现发现效果图实现未读红点未读条数朋友圈效果图实现上拉加载个人设置效果图实现个人信息功能区钱包效果图实现切换主题…

【Vue】项目搭建规范

1. 集成editorconfig配置 EditorConfig 有助于为不同 IDE 编辑器上处理同一项目的多个开发人员维护一致的编码风格。 VSCode需要安装一个插件&#xff1a;EditorConfig for VS Code 创建 .editorconfig 文件&#xff1a; # http://editorconfig.orgroot true[*] # 表示所有文…

js Proxy 的使用

文章目录一、什么是Proxy二、语法三、Proxy 方法1、get() 方法2、set() 方法3、apply() 方法4、has() 方法5、construct() 方法6、deleteProperty() 方法一、什么是Proxy Proxy 可以理解成&#xff0c;在目标对象之前架设一层“拦截”&#xff0c;外界对该对象的访问&#xff…

[Vulnhub] DC-2

Vlunhub下DC系列靶机第二台&#xff0c;难度与DC-1 差不多&#xff0c;为简单。共有五个Flag 下载地址&#xff1a;Vulnhub:DC-2 目录 信息搜集 cewl爬行网站字典&hydra爆破wordpress用户密码 -rbash逃逸 git提权 信息搜集 nmap -sP 192.168.236.0/24 扫描一下靶机i…

c++语法欠缺地方(持续更新)

sizeof是用来计算变量占多大内存的&#xff0c;单位是字节&#xff08;byte&#xff09;&#xff1b;sizeof 后面跟类型时&#xff0c;必须加上括号&#xff0c;例如sizeof(double);后面跟变量可以不用加括号&#xff0c;例如&#xff1a;sizeof d %d是以十进制形式输出有符号…

hadoop之kerberos权限配置(ranger基础上)(三)

文章目录一、kerberos服务端二、kerberos客户端三、hadoop集群安装HTTPS服务四、kerberos整合zk五、kerberos整合ranger六、kerberos整合hdfs七、kerberos整合yarn八、kerberos整合hive九、kerberos整合hbase十、遇到的问题一、kerberos服务端 上传kerberos安装包到/opt/rpm 安…

数据库,计算机网络、操作系统刷题笔记22

数据库&#xff0c;计算机网络、操作系统刷题笔记22 2022找工作是学历、能力和运气的超强结合体&#xff0c;遇到寒冬&#xff0c;大厂不招人&#xff0c;可能很多算法学生都得去找开发&#xff0c;测开 测开的话&#xff0c;你就得学数据库&#xff0c;sql&#xff0c;oracle…

最值得推荐的3个免费PDF 转换器

当您需要将 PDF 转换为另一种格式&#xff08;如 Microsoft Word、图像&#xff08;如 JPG&#xff09;、Excel、电子书、PowerPoint 等&#xff0c;反之亦然&#xff09;时&#xff0c;最好的 PDF 转换器非常重要。 但是找到一个可靠的 PDF 转换软件来使用是具有挑战性的。因…

JAVA设计模式--行为型模式--策略模式

1.策略模式&#xff08;Strategy Pattern&#xff09; 1.1介绍 一个类的行为或其算法可以在运行时更改。这种类型的设计模式属于行为型模式。 在策略模式中&#xff0c;我们创建表示各种策略的对象和一个行为随着策略对象改变而改变的 context 对象。策略对象改变 context 对…

【C++】网络编程(TCPUDP)

网络编程是C API操作中很重要的一部分&#xff0c;包含TCP和UDP。 网络传输模型可以抽象为7个层&#xff1a;物理层、数据链路层、网络层、传输层、会话层、表示层、应用层。 但在使用TCP/IP协议时&#xff0c;可以简化为这4层&#xff1a;网络接口、网络层、传输层、应用层。…

物理层基本概念

目录物理层的基本概念物理层传输方式串行传输并行传输同步传输异步传输单向通信&#xff08;单工&#xff09;双向交替通信&#xff08;半双工&#xff09;双向同时通信&#xff08;全双工&#xff09;编码与调制常用编码基本调制方法信道极限容量物理层的基本概念 物理层考虑的…

路由 OSPF常见4种网络类型MA、P2P、NBMA、P2MP、OSPF报头字段信息简介。

4.2.1 路由 OSPF&#xff08;OSPF常见4种网络类型、OSPF报头信息&#xff09; 目录OSPF常见的4种网络类型广播类型&#xff08;Broadcast 或 MA&#xff09;P2PNBMAP2MPOSPF报文发送形式对于不同OSPF网络类型的组网OSPF报头信息实际抓包分析OSPF常见的4种网络类型 OSPF应用于不…

Kali最强渗透工具- metasploit

数据来源 本文仅用于信息安全学习&#xff0c;请遵守相关法律法规&#xff0c;严禁用于非法途径。若观众因此作出任何危害网络安全的行为&#xff0c;后果自负&#xff0c;与本人无关。 metasploit是什么&#xff1f; msf是一款开源安全漏洞利用和测试工具&#xff0c;集成了…

C++GUI之wxWidgets(9)-编写应用涉及的类和方法(4)-事件处理(3)

目录动态事件处理如何处理事件事件如何向上传播事件处理程序链动态事件处理 void MyFrameHandler::OnFrameExit(wxCommandEvent&) {// Do something useful. }MyFrameHandler myFrameHandler;MyFrame::MyFrame() {Bind(wxEVT_MENU, &MyFrameHandler::OnFrameExit,&…

Java数组的定义与使用

Java数组的定义与使用 文章目录Java数组的定义与使用数组的基本概念什么是数组数组的创建数组的初始化数组的使用数组中元素访问遍历数组数组是引用类型初始JVM的内存分布基本类型变量与引用类型变量的区别引用变量几道例题认识null数组的应用场景保存数据作为函数的参数参数传…

TypeScript中的泛型

泛型&#xff08;Generics&#xff09;是指在定义函数、接口或类的时候&#xff0c;不预先指定具体的类型&#xff0c;而在使用的时候再指定类型的一种特性。 通常用T来指代任意输入的类型&#xff0c;除了T之外&#xff0c;以下是常见泛型变量代表的意思&#xff1a; K(Key…

信息安全技术 可信计算规范 可信平台控制模块 学习笔记(一)

声明 本文是学习信息安全技术 可信计算规范 可信平台控制模块. 下载地址而整理的学习笔记,分享出来希望更多人受益,如果存在侵权请及时联系我们 可信计算规范 缩略语 下列缩略语适用于本文件。 I/O&#xff1a;输入输出&#xff08;Input/Output&#xff09; IP&#xff1…

第三十八章 贪心算法——区间问题及证明(上)

第三十八章 贪心策略——区间相关问题一、什么贪心策略&#xff1f;二、区间问题合集1、思路&#xff1a;2、问题1&#xff1a; 区间选点&#xff08;1&#xff09;问题&#xff08;2&#xff09;思路和证明a.思路b.证明&#xff08;3&#xff09;代码3、问题2&#xff1a;&…

java:文件分片上传

代码下载地址&#xff1a;https://download.csdn.net/download/u013938578/87358484 1 文件上传、断点续传服务端 1.1 新建maven项目 文件结构如下&#xff1a; 1.2 引入百度开源上传组件webuploader 1.3 前端页面upload.html <!DOCTYPE html> <html lang"en&…