并查集python实现及题目练习

文章目录

  • 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

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【一】学习TDengine-总结新技术学习的思考

学习TDengine-总结新技术学习的思考 概要 因业务场景需要我们开始接触时序数据库&#xff0c;于是开始根据以往的学习经验着手熟悉这一项新技术&#xff0c;学习也是一种技能&#xff0c;成功的人越容易成功&#xff0c;因为他们掌握了一套成功的方法&#xff0c;这里提到学习经…

【LeetCode热题100】74. 搜索二维矩阵(二分)

一.题目要求 给你一个满足下述两条属性的 m x n 整数矩阵&#xff1a; 每行中的整数从左到右按非严格递增顺序排列。每行的第一个整数大于前一行的最后一个整数。 给你一个整数 target &#xff0c;如果 target 在矩阵中&#xff0c;返回 true &#xff1b;否则&#xff0c;…

清明寄哀思,VR云祭扫沉浸式缅怀先烈

只要拿出手机扫一扫&#xff0c;就能通过VR全景&#xff0c;沉浸式走进烈士陵园、纪念场馆&#xff0c;随场景进行同步参观&#xff0c;进行“云祭扫”。这种“云祭扫”活动一经推出就受到了广大群众的追捧&#xff0c;VR全景云祭扫以一种全新、绿色、安全的理念&#xff0c;通…

别让.[[hashtreep@waifu.club]].svh勒索病毒盯上你:一份实用的科普与防范经验

引言&#xff1a; 在数字化浪潮席卷全球的今天&#xff0c;我们享受着信息技术带来的便捷与高效。然而&#xff0c;随着我们对网络的依赖越来越深&#xff0c;网络安全问题也日益凸显。其中&#xff0c;.[[hashtreepwaifu.club]].svh勒索病毒就是一种让人闻之色变的网络威胁。它…

Mac安装Docker提示Another application changed your Desktop configuration解决方案

1. 问题描述 Mac安装Docker后&#xff0c;提示Another application changed your Desktop configuration&#xff0c;Re-apply configurations无效 2. 解决方案 在终端执行下述命令即可解决&#xff1a; sudo ln -sf /Applications/Docker.app/Contents/Resources/bin/docke…

2024/4/1—力扣—两数相除

代码实现&#xff1a; 思路&#xff1a;用减法模拟除法 // 用减法模拟除法 int func(int a, int b) { // a、b均为负数int ans 0;while (a < b) { // a的绝对值大于等于b&#xff0c;表示此时a够减int t b;int count 1; // 用来计数被减的次数// t > INT_MIN / 2:防止…

Tire树

Trie 树是一种多叉树的结构&#xff0c;每个节点保存一个字符&#xff0c;一条路径表示一个字符串。 Trie字符串统计 维护一个字符串集合&#xff0c;支持两种操作&#xff1a; I x 向集合中插入一个字符串 x&#xff1b;Q x 询问一个字符串在集合中出现了多少次。 共有 N 个…

突破外贸挑战:推荐几款优秀的CRM软件,解析解决方案

外贸企业面临的困境愈演愈烈&#xff0c;他们不仅面临着外部竞争对手以及市场的挑战&#xff0c;内部还面临着资金和管理难题的挤压。墨守成规&#xff0c;还按照之前单一的管理运营模式&#xff0c;迟早会被市场淘汰。 现在的市场趋势是企业要逐步走向精细化管理&#xff0c;将…

蓝牙学习十(扫描)

一、简介 从之前的文章中我们知道&#xff0c;蓝牙GAP层定义了四种角色&#xff0c;广播者&#xff08;Broadcaster&#xff09;、观察者&#xff08;Observer&#xff09;、外围设备&#xff08;Peripheral&#xff09;、中央设备&#xff08;Central&#xff09;。 之前的学习…

华为ICT七力助推文化产业新质生产力发展

创新起主导作用的新质生产力由新劳动者、新劳动对象、新劳动工具、新基础设施等四大要素共同构成&#xff0c;符合新发展理念的先进生产力质态&#xff1b;具有高科技、高能效、高质量等三大突出特征。而通过壮大新产业、打造新模式、激发新动能&#xff0c;新质生产力能够摆脱…

JMeter源码解读 - HashTree(超详细)

&#x1f345; 视频学习&#xff1a;文末有免费的配套视频可观看 &#x1f345; 关注公众号【互联网杂货铺】&#xff0c;回复 1 &#xff0c;免费获取软件测试全套资料&#xff0c;资料在手&#xff0c;涨薪更快 在 JMeter 中&#xff0c;HashTree 是一种用于组织和管理测试计…

网络安全意识也是基础防御中的关键一环

在当今数字化时代&#xff0c;网络安全已经成为企业和个人生活中不可或缺的一部分。网络攻击的不断演进和加剧使得保护个人隐私、商业机密和国家安全变得尤为重要。然而&#xff0c;网络安全并非仅仅是技术层面的问题&#xff0c;更是一个综合性的挑战&#xff0c;需要广泛的参…

场景文本检测识别学习 day01(传统OCR的流程、常见的损失函数)

传统OCR的流程 传统OCR&#xff1a;传统光学字符识别常见的的模型主要包括以下几个步骤来识别文本 预处理&#xff1a;预处理是指对输入的图像进行处理&#xff0c;以提高文字识别的准确率。这可能包括调整图像大小、转换为灰度图像、二值化&#xff08;将图像转换为黑白两色&…

Android视角看鸿蒙第十一课-鸿蒙的布局之层叠布局Stack

Android视角看鸿蒙第十一课-鸿蒙的布局之层叠布局 导读 在Android中我个人认为&#xff0c;最离不开的就是LinearLayout和FrameLayout了&#xff0c;RelativeLayout我都基本不用的。 所以我把层叠布局排在了第二位。 官方描述 如何定义层叠布局 Stack组件为容器组件&#x…

瑞登梅尔邀您参观2024第13届生物发酵展精彩不容错过

参展企业介绍 我们&#xff0c;瑞登梅尔(上海)纤维贸易有限公司 致力于研究、开发和生产以植物为原料的高品质有机纤维。我们让这些有价值的天然物质的许多特性&#xff0c;能够广泛应用于现代工业的各个领域。 JRS的纤维产品是由天然的、可再生的原料制成。例如&#xff1a;…

泛微OA 自定义多选浏览框

1、建模引擎-》应用建模-》表单 2、建模引擎-》应用建模-》模块 3、建模引擎-》应用建模-》查询 4、把查询页面挂到前端页面。 效果展示&#xff1a; 5、建模引擎-》应用建模-》浏览框 6、流程表单中字段应用

遥感影像处理利器:PyTorch框架下CNN-Transformer,地物分类、目标检测、语义分割和点云分类

目录 专题一 深度卷积网络知识详解 专题二 PyTorch应用与实践&#xff08;遥感图像场景分类&#xff09; 专题三 卷积神经网络实践与目标检测 专题四 卷积神经网络的遥感影像目标检测任务案例【FasterRCNN】 专题五 Transformer与遥感影像目标检测 专题六 Transformer的遥…

中药配方颗粒备案信息数据库<2.5W+备案>

中药配方颗粒备案信息是指中药配方颗粒生产企业向国家药品监督管理局申报备案的相关信息。备案信息包括中药配方颗粒的名称、备案号、备案时间、备案状态、生产企业、生产地址、规格、包装、执行标准、保质期、不良反应检测、备案省局等信息。 通过对中药配方颗粒备案信息的查…

完成产品兼容互认,用KubeBlocks可实现OceanBase集群管理

本文转载自云猿生聊技术&#xff08;CloudNativeDataTech&#xff09; 前言 KubeBlocks&#xff08;简称 KB&#xff09;在最新发布的0.7版本中&#xff0c;通过组件扩展&#xff08;Addon&#xff09;的形式新增了对OceanBase的支持功能。这一更新为企业级和非企业级用户提供…

数据结构6.2:二叉树的前中后序遍历

二叉树前中后序遍历代码实现 二叉树的遍历 二叉树的前序遍历 先输出父节点,再遍历左子树和右子树 二叉树的中序遍历 先遍历左子树,再输出父节点,再遍历右子树 二叉树的后序遍历 先遍历左子树,再遍历右子树,最后输出父节点 代码实现 每个节点中包含一个数据域和两个地址…