并查集python实现及题目练习

news2024/11/20 15:14:19

文章目录

  • 1. 并查集概念
    • 1.1 理解并查集:简介与应用场景
    • 1.2 Python 实现并查集及优化策略
    • 1.3 扁平化栈实现
    • 1.4 分析并查集的时间复杂度
  • 2. 情侣牵手
  • 3. 相似字符串
  • 4. 岛屿数量

如果想了解并查集基础推荐去看左程云大神的算法讲解,非常不错,b站和油管上都有它的视频

1. 并查集概念

1.1 理解并查集:简介与应用场景

概述:
并查集(Disjoint Set)是一种用于处理集合合并和查询等问题的数据结构。

并查集的作用:

  • 解决元素分组与连接问题
  • 用于图论中的最小生成树算法、连通性问题、网络分区等

1.2 Python 实现并查集及优化策略

class UnionFind:
    def __init__(self, n):
        # 初始化时,每个元素的父节点为自己
        self.parent = list(range(n))
        # 初始化时每个树的深度为0(也可以设置为1)
        self.rank = [0] * n

    # 查找操作(扁平化)
    def find(self, x):
        if self.parent[x] != x:  # 如果当前节点的父节点不是自己,说明不是根节点
            # 路径压缩,将当前节点直接连接到根节点
            self.parent[x] = self.find(self.parent[x])
        return self.parent[x]  # 返回根节点的索引

    # 合并操作(小挂大)
    def union(self, x, y):
        root_x = self.find(x)  # 找到元素 x 的根节点
        root_y = self.find(y)  # 找到元素 y 的根节点

        if root_x != root_y:  # 如果两个元素不在同一个集合中
            if self.rank[root_x] < self.rank[root_y]:  # 如果树的深度(秩)小于另一个树
                # 将较浅的树的根节点连接到较深的树的根节点
                self.parent[root_x] = root_y
            elif self.rank[root_x] > self.rank[root_y]:
                self.parent[root_y] = root_x
            else:  # 如果两个树深度相同,则任意连接一个到另一个,并将深度加一
                self.parent[root_y] = root_x
                self.rank[root_x] += 1

优化策略:

  • 路径压缩优化(扁平化):在查找操作中,将节点直接连接到根节点,降低树的深度
  • 按秩合并优化(小集合挂大集合):将深度较浅的树挂在深度较深的树下,保持树的平衡

1.3 扁平化栈实现

def find(self, x):
    # 使用栈记录路径
    path = []
    # 找到根节点
    while self.parent[x] != x:
        path.append(x)
        x = self.parent[x]

    # 路径压缩:将路径上的所有节点直接连接到根节点
    for node in path:
        self.parent[node] = x
    return x

1.4 分析并查集的时间复杂度

时间复杂度分析:

  • 查找操作(Find):近似为 O(1)。
  • 合并操作(Union):近似为 O(1)。
  • 综合时间复杂度:近似为 O(α(n)),其中 α(n) 是 Ackermann 函数的反函数,通常视为常数级别。

2. 情侣牵手

测试链接:https://leetcode.cn/problems/couples-holding-hands/

image-20240406161605370

image-20240406161613003

  1. 分析
分析:将每对情侣的对数看成一个集合,例如:第一对情侣(0,1),属于集合{0};第二对情侣(2,3),属于集合{1}...
	 所以最开始一定有n个集合

思路:就是每次遍历相邻的两个人,如果它们不是一对,那么它们就分别属于各自的那一对,将这两对的集合合并,此时总集合数就少1

求解答案(两种):
	1. 对于每一个集合,如果里面有m对情侣,那么就需要交换m-1次
	2. 总的集合数减去合并后的集合数
    
例子,[0,4,2,1,3,5]:
	- 一共3对情侣,3个集合,{0},{1},{2}
	- 遍历0和4,0//2 != 4//2,它们不是一对,将它们的集合合并,此时有2个集合:{0,2},{1}
	- 遍历2和1,2//2 != 1//2,它们不是一对,将它们的集合合并,此时有1个集合:{0,2,1}
	- 所以一共需要交换:
		1. 只有1个集合,这个集合有3对情侣,需要交换3-1=2次
		2. 原来有3个集合,现在只有1个集合,需要交换3-1=2次
  1. 代码
class UnionFind:
    def __init__(self, n):
        self.father = [0] * n
        # 初始化集合
        for i in range(n):
            self.father[i] = i
        # 记录集合的数量
        self.sets = n

    def find(self, x):
        if self.father[x] != x:
            self.father[x] = self.find(self.father[x])
        return self.father[x]
	
    def union(self, x, y):
        fx = self.find(x)
        fy = self.find(y)
		
        # 如果它们的father不相同,说明不是一对情侣,需要合并
        if fx != fy:
            self.father[fx] = fy
            # 合并后集合数-1
            self.sets -= 1


class Solution(object):
    def minSwapsCouples(self, row):
        """
        :type row: List[int]
        :rtype: int
        """
        n = len(row)
        couple = UnionFind(n // 2)
        for i in range(0, n - 1, 2):
            # 依次遍历两个人,是一对情侣就不合并,不是一对情侣就合并
            couple.union(row[i] // 2, row[i + 1] // 2)
        return n // 2 - couple.sets

image-20240406165156737

3. 相似字符串

测试链接:https://leetcode.cn/problems/H6lPxb/

image-20240406201614718

image-20240406201623530

  1. 题目分析
1. 什么是字母异位词?
   假如给一个字符串abc,它的字母异位词有acb,bac,bca,cab,cba。当然,abc也同样是它们的字母异位词

2. 什么是相似?
   给定两个字符串x,y(都由小写字母组成),分两种情况:
   - 如果x和y不做任何操作就相同,那它们相似
   - 如果x和y不同,交换x中任意两个字母的位置能变成y,就说明x和y相似

3. 什么是相似字符串组?
   根据示例1,strs=['tars','rats','arts','star']
   - 首先它们四个各自为一组,共四个组
   - strs[0]和strs[1]相似,把它们分到一组
   - strs[0]和strs[2]不相似,不把strs[2]加入到strs[0]那一组
   - strs[0]和strs[3]不相似,不把strs[3]加入到strs[0]那一组
   - strs[1]和strs[2]相似,把strs[2]加入到strs[1]那一组
   - strs[1]和strs[3]不相似,不把strs[3]加入到strs[1]那一组
   - ...
   - strs[3]和前面的字符串都不相似,自己单独为一组
   - 最后就分为了两个组,{strs[0],strs[1],strs[2]},{strs[3]}
   - 很明显地就是用并查集去分组
   - 只要剩余的字符串与一个组内的任一字符串相似就将它们合并
  1. 代码
class UnionFind:
    def __init__(self, n):
        self.father = [0] * n
        self.size = [1] * n
        for i in range(n):
            self.father[i] = i
        self.sets = n

    def find(self, x):
        if self.father[x] != x:
            self.father[x] = self.find(self.father[x])
        return self.father[x]
    
    # 小挂大
    def union(self, x, y):
        fx = self.find(x)
        fy = self.find(y)
        if fx != fy:
            if self.size[fx] >= self.size[fy]:
                self.father[fy] = fx
                self.size[fx] += self.size[fy]
            else:
                self.father[fx] = fy
                self.size[fy] += self.size[fx]
            self.sets -= 1

class Solution(object):
    def numSimilarGroups(self, strs):
        """
        :type strs: List[str]
        :rtype: int
        """
        # 共有n个字符串
        n = len(strs)
        # 每个字符串长度为m
        m = len(strs[0])
        connect = UnionFind(n)
        for i in range(n):
            for j in range(i + 1, n):
                # 两两比较字符串,如果它们不在同一个组中,并且它们相似,就合并
                if (connect.find(i) != connect.find(j)):
                    diff = 0
                    # 依次比较strs[i]和strs[j],因为strs中的字符串都互为字母异位词
                    # 所以只需要看strs[i]和strs[j]有几个字符不一样就行了
                    for k in range(m):
                        if strs[i][k] != strs[j][k]:
                            diff += 1
                        # strs[i]要么完全和strs[j]一样,要么有两个字符不同,这样才满足相似的条件
                        if diff >= 3:
                            break
                    # 如果相似,就合并
                    if diff == 0 or diff == 2:
                        connect.union(i, j)

        return connect.sets

image-20240406203301364

4. 岛屿数量

测试链接:https://leetcode.cn/problems/number-of-islands/

image-20240406211948653

image-20240406211958246

  1. 分析
思路:使用并查集的话就是先将每个1都视为1个集合,如果它的上下左右也是1,就合并两个集合

下标转换:将二维下标转换为一维下标,例如下面是一个4×4的二维表,坐标[i][j]转换为一维的就是i*m + j(m为列的数量)
  n\m 0 1 2 3
   0  0 1 2 3
   1  4 5 6 7
   2  8 9 10 11
   3  12 13 14 15
  1. 代码
class UnionFind:
    def __init__(self, n, m, grid):
        # 正常建立并查集,我们只需要关注“1”就行了,“0”不用管
        self.father = [-1] * ((n - 1) * m + m)
        self.sets = 0
        for i in range(n):
            for j in range(m):
                if grid[i][j] == '1':
                    self.father[i * m + j] = i * m + j
                    self.sets += 1
    
    def find(self, x):
        if self.father[x] != x:
            self.father[x] = self.find(self.father[x])
        return self.father[x]

    def union(self, x, y):
        fx = self.find(x)
        fy = self.find(y)
        if fx != fy:
            self.father[fy] = fx
            self.sets -= 1


class Solution(object):
    def numIslands(self, grid):
        """
        :type grid: List[List[str]]
        :rtype: int
        """
        n = len(grid)
        m = len(grid[0])
        area = UnionFind(n, m, grid)
        for i in range(n):
            for j in range(m):
                if grid[i][j] == '1':
                    # “0”不用管的原因是因为我们合并x,y的时候,x,y对应的二维坐标在二维表中一定是“1”
                    # 下面只需检查每个“1”的左上是否为1,就不用上下左右全部检查了
                    if j > 0 and grid[i][j - 1] == '1':
                        area.union(i * m + j, i * m + j - 1)
                    if i > 0 and grid[i - 1][j] == '1':
                        area.union(i * m + j, (i - 1) * m + j)
        return area.sets

image-20240406212823666

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

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

相关文章

InterlliJ Debug方式启动 method breakpoints may dramatically show down debugging

使用idea在DEBUG的时候出现Method breakpoints may dramatically slow down debugging&#xff0c; 如图&#xff1a; 根据语义可能是断点打在方法上面了&#xff0c;导致在某个断点卡住了。 重启服务器和重启idea已然无解。 打开Breakpoints面板看看&#xff0c;(快捷键&…

递归实现排列型枚举(acwing)

题目描述&#xff1a; 把 1∼n 这 n 个整数排成一行后随机打乱顺序&#xff0c;输出所有可能的次序。 输入格式&#xff1a; 一个整数 n。 输出格式&#xff1a; 按照从小到大的顺序输出所有方案&#xff0c;每行 1 个。 首先&#xff0c;同一行相邻两个数用一个空格隔开…

mac、windows 电脑安装使用多个版本的node

我们为啥要安装多个不同版本的node&#xff1f; 开发旧项目时&#xff0c;使用低版本Nodejs。开发新项目时&#xff0c;需使用高版本Node.js。可使用n同时安装多个版本Node.js&#xff0c;并切换到指定版本Node.js。 mac电脑安装 一、全局安装 npm install -g n 二、mac电脑…

淘宝优惠券领取软件叫什么?

草柴返利APP是一款淘宝优惠券领取软件。用户可以通过草柴淘宝优惠券领取软件轻松查找领取淘宝大额隐藏优惠券&#xff0c;领取成功后再购物可享受券后价优惠。同时&#xff0c;通过草柴APP领券购买成功&#xff0c;确认收货后再回到草柴APP提取购物返利&#xff0c;享受双重省钱…

idea maven 打包 内存溢出 报 GC overhead limit exceeded -> [Help 1]

idea 使用maven打包 报GC overhead limit exceeded -> [Help 1] 解决方法&#xff1a; 打开settings -> 点开如同所示 将 vm Options 参数 设为 -Xmx8g

记第一次eudsrc拿到RCE(下)

目录 前言 个人介绍 挖洞公式 漏洞介绍 信息泄露漏洞 任意文件读取漏洞 远程命令执行&#xff08;RCE&#xff09;漏洞 漏洞详情 漏洞点1 漏洞点2 漏洞点3 修复建议 总结 前言 免责声明 以下漏洞均已经上报漏洞平台。请勿利用文章内的相关技术从事非法测试。若因…

Autosar BswM 模式管理

EcuMs管理ECU上下电状态,BswM管理模式,协同工作。当使用EcuM - Fixed时,它将向BswM指示当前ECU状态 有了BswM,从图可以更加直观看出,BswM管理各个模块,每个模块独立,降低耦合。 BswM 的主要功能包括: 模式管理:BswM 可以管理和控制 ECU 的不同模式,例如正常模式、备…

AJAX 原理

一、AJAX原理 - XMLHttpRequest 定义&#xff1a; 关系&#xff1a;axios 内部采用 XMLHttpRequest 与服务器交互。 好处&#xff1a;掌握使用 XHR 与服务器进行数据交互&#xff0c;了解 axios 内部原理。 1.1 使用 XMLHttpRequest&#xff1a; 步骤&#xff1a; 1. 创建 XM…

免注册,ChatGPT可即时访问了!

AI又有啥进展&#xff1f;一起看看吧 Apple进军个人家用机器人 Apple在放弃自动驾驶汽车项目并推出混合现实头显后&#xff0c;正在进军个人机器人领域&#xff0c;处于开发家用环境机器人的早期阶段 报告中提到了两种可能的机器人设计。一种是移动机器人&#xff0c;可以跟…

【机器学习300问】60、图像分类任务中,训练数据不足会带来什么问题?如何缓解图像数据不足带来的问题?

在机器学习中&#xff0c;绝大部分模型都需要大量的数据进行训练和学习&#xff08;包括有监督学习和无监督学习&#xff09;&#xff0c;然而在实际应用中经常会遇到训练数据不足的问题。就比如图像分类这样的计算机视觉任务&#xff0c;确实依赖于大规模且多样化的训练数据以…

Java笔试总结

. 操作系统中关于竞争和死锁的关系下面描述正确的是&#xff1f; A 竞争一定会导致死锁 B 死锁一定由竞争引起 C 竞争可能引起死锁 D 预防死锁可以防止竞争 答案: C 进程的控制信息和描述信息存放在()。 A JCB B PCB C AFT D SFT 答案: B 当系统发生抖动&#xff08;thrash…

企业数据资产入表的基本原则、参与主体和一般实现路线

目录 引言 一、企业数据资产入表的基本原则 1、准确性原则 2、一致性原则 3、安全性原则 4、可扩展性原则 5、合法合规原则 6、谨慎性原则 7、商业秘密保护原则 二、企业数据资产入表的参与主体 1、企业内部参与部门 2、企业外部参与机构 三、企业数据资产入表的一…

瑞_Redis_商户查询缓存_添加Redis缓存缓存更新策略

文章目录 项目介绍1 短信登录2 商户查询缓存2.1 什么是缓存2.1.1 缓存的应用场景2.1.2 为什么要使用缓存2.1.3 Web应用中缓存的作用2.1.4 Web应用中缓存的成本 2.2 添加Redis缓存2.2.1 背景2.2.2 缓存模型和思路2.2.3 代码实现2.2.4 测试附&#xff1a;IDEA控制台输出自动换行设…

阿里云2核2G和2核4G轻量应用服务器优惠价格表,2024年最新报价

阿里云轻量应用服务器2核2G和2核4G配置优惠价格表&#xff0c;轻量2核2G3M带宽61元一年&#xff0c;轻量2核4G4M带宽165元1年&#xff0c;均不限制月流量&#xff0c;阿里云活动链接 aliyunfuwuqi.com/go/aliyun 活动打开如下图&#xff1a; 阿里云轻量应用服务器价格 61元/年…

《QT实用小工具·七》CPU内存显示控件

1、概述 源码放在文章末尾 CPU内存显示控件 项目包含的功能如下&#xff1a; 实时显示当前CPU占用率。实时显示内存使用情况。包括共多少内存、已使用多少内存。全平台通用&#xff0c;包括windows、linux、ARM。发出信号通知占用率和内存使用情况等&#xff0c;以便自行显示…

ubuntu-server部署hive-part1-安装jdk

参照 https://blog.csdn.net/qq_41946216/article/details/134345137 操作系统版本&#xff1a;ubuntu-server-22.04.3 虚拟机&#xff1a;virtualbox7.0 安装jdk 上传解压 以root用户&#xff0c;将jdk上传至/opt目录下 tar zxvf jdk-8u271-linux-x64.tar.gz 配置环境变量…

2_6.Linux高级存储管理

##1.逻辑卷## pv ##物理卷 被处理过的物理分区 pe ##物理扩展 设定存储最小单元 vg ##物理卷组 捆绑pv到一个组中 lv ##逻辑卷 分配最终的使用设备 监控建立过程&#xff1a; watch -n 1 "pvs;echo ;vgs;echo ;lvs;echo ;df -h /weixindata" &#xff08;1&#xf…

【C++ STL有序关联容器】map 映射

文章目录 【 1. 基本原理 】【 2. map 的创建 】2.1 调用默认构造函数&#xff0c;创建一个空的 map2.2 map 被构造的同时初始化2.3 通过一个 queue 初始化另一个 queue2.4 取已建 map 中指定区域内的键值对&#xff0c;初始化新的 map2.5 指定排序规则 【 2. map 元素的操作 】…

知识融合:知识图谱构建的关键技术

目录 一、引言二、知识图谱基础2.1 知识表示三元组属性图 2.2 知识抽取实体抽取关系抽取属性抽取 三、知识融合的核心问题3.1 实体识别与链接实体识别实体链接 3.2 重复实体合并方法示例 3.3 关系融合挑战方法示例 四、知识融合技术深度解析4.1 基于规则的方法规则设计原则规则…

噪声的力量:重新定义 RAG 系统的检索

该文得到了一个反常识的结论&#xff0c;当无关的噪声文档放在正确的位置时&#xff0c;实际上有助于提高RAG的准确性。 摘要 检索增强生成&#xff08;RAG&#xff09;系统代表了传统大语言模型&#xff08;大语言模型&#xff09;的显着进步。 RAG系统通过整合通过信息检索…