并查表(认老大的区分方式)

2023-10-21 04:10
文章标签 方式 区分 查表 老大

本文主要是介绍并查表(认老大的区分方式),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

文章目录

  • 一、并查表的用处
  • 二、并查表的构成
  • 三、例题剖析
    • 为什么要使用并查表来解决此题?
  • 提升


一、并查表的用处

一般由于检测图的连通性问题。给你一幅图,和图中两个结点,请问这两个结点是否相连通?或者检测该图中是否有环。当然还可解决其他问题。

二、并查表的构成

由find函数,join函数,以及对应的集合的代表结点数组构成。

像黑社会这种组织,都是老大说了算,老大的手下都是跟着老大做事,可以这么说老大一定程度上是手下的代表,一个老大代表了他所有的手下。将元素都归于集合中,这就是并查表的主要思想。一个人也可以是一个黑社会团体哦
如果对集合概念了解的,可以理解为,将一个个结点(手下)放入到不同的集合(黑社会团体)中,对应的集合的代表结点数组 表示不同的集合(用集合中的一个元素代表该集合)(老大代表了黑社会团体),join函数 的作用是将结点放入到集合中,亦或者将两个集合并操作(两个黑社会黑吃黑,一个黑社会吞并另一个黑社会)。find函数 的作用是给一个结点,找到该结点对应集合的代表结点。(给一个手下找到他所属黑社会的老大)

三、例题剖析

  1. 冗余连接
    在本问题中, 树指的是一个连通且无环的无向图。
    输入一个图,该图由一个有着N个节点 (节点值不重复1, 2, …, N) 的树及一条附加的边构成。附加的边的两个顶点包含在1到N中间,这条附加的边不属于树中已存在的边。
    结果图是一个以边组成的二维数组。每一个边的元素是一对[u, v] ,满足 u < v,表示连接顶点u 和v的无向图的边。
    返回一条可以删去的边,使得结果图是一个有着N个节点的树。如果有多个答案,则返回二维数组中最后出现的边。答案边 [u, v] 应满足相同的格式 u < v。

题目来源:leetcode 684 冗余连接
题目要求可简化为,该无向图中含有一环,请找出该环中一个连接。

解该题的方法有很多,这里只写出使用并查表的解决方案。
举一个例子:
假设输入是:

[[1,4],[3,4],[1,3],[1,2],[4,5]]

可视化的图
在这里插入图片描述
很明显,1 - 4 - 3 是一个环,按道理来讲, 返回[1 , 4],[3, 4] 或者 [1, 3] 都可,而题目中要求 如果有多个答案,则返回二维数组中最后出现的边说明只需要返回[1, 3]。

接下来推断:
我们先创建一个数组 假设索引从1开始,不是0,该数组的索引是指对应的结点,而其值就是结点所属的集合。集合的表示方法用集合元素中的首项来表示该集合

        # 声明一个数组root_list = [i for i in range(1, len(edges) + 1)]

可以看出一开始每一个结点的结合都只有自己。结点1所属集合只有1一个元素,结点2所属集合只含2一个元素…

find函数的实现

“”“参数:tar 	图中的一个结点的值nums	记录老大数组返回:该结点对应的集合,并返回该集合代表的结点
”“”def find(tar, nums):while tar != nums[tar]:tar = nums[tar]return tar

主函数和join函数的实现:

 for item in edges:# 找到两边结点所属的集合set_1 = find(item[0], root_list)set_2 = find(item[1], root_list)# 如果边开头和边结尾的元素都是同一个集合,说明一定是一个环if set_1 == set_2:return item# 当发现双方所含的集合不一样的时候,选择其中一个集合,另一个集合加入该集合。else:root_list[set_2] = set_1

步骤如下:
root_list:[1, 2, 3, 4, 5]

第一步:
读取[1, 4]
1的集合只含1,4的集合只含4。
发现所属集合不一样,4的集合加入1的集合
所以root_list变化为:[1, 2, 3, 1, 5]

第二步:
读取[3, 4]
3所属集合有3,4所属集合有1,4(用1表示该集合)
发现集合不一样, 4所属的结合加入3的集合中 (4所属的集合的代表是 1,所以数组中第一位的值变为3,说明1集合和3集合合并,那么3集合所包含的元素就有3,1,4)
所以root_list变化为:[3, 2, 3, 1, 5]

第三步:
读取[1, 3]
发现1和3的属于同一个集合,返回该数组[1, 3]为所求。

可能会有疑问,为什么第二步最后的root_list变更是[3, 2, 3, 1, 5],而不是[3, 2, 3, 3, 5]呢?
1,3,4同属于一个集合中为什么root_list会不一样?当然我们也是想一样的,因为这样的话,find函数就不需要了,直接访问root_list就好了,其索引对应的值就是所属集合的代表。可是我们很难做到,或者说要花费沉重的代价去实现。所以find函数,帮我们节省这些操作。当[3, 2, 3, 1, 5]传入find函数,想找到第四项所属的集合的代表,访问第四项,对应的值1,然后判断1跟索引值4不相同,说明1不是一个集合的代表,然后将1作为索引,访问第一项,第一项的值为3,判断跟索引1不相同,说明3不是一个集合的代表,然后将3作为索引,访问第三项,发现索引值3和其索引对应的值也是3,相同,说明3是一个集合的代表。推出4所属的集合的代表为3。

明白以上操作后,总代码仅仅只是将索引调整了一下,因为索引从0开始:


class Solution(object):def findRedundantConnection(self, edges):""":type edges: List[List[int]]:rtype: List[int]"""def find(tar, nums):while tar != nums[tar - 1]:tar = nums[tar - 1]return tar# 声明一个结点数组root_list = [i for i in range(1, len(edges) + 1)]for item in edges:print("  item 0 :", item[0], "  item 1 :", item[1])set_1 = find(item[0], root_list)set_2 = find(item[1], root_list)print(set_1, set_2)print(root_list)if set_1 == set_2:return item# 进行合并else:root_list[set_2 - 1] = set_1if __name__ == '__main__':test_class = Solution()print(test_class.findRedundantConnection([[1, 4], [3, 4], [1, 3], [1, 2], [4, 5]]))

为什么要使用并查表来解决此题?

假设我们要解决的问题是,给定两个结点,判断该结点是否连通。使用并查集的思路是:将连通第一个结点的所有结点都放入一个集合中,然后看看,另一个结点是否也在该集合中,如果存在就说明连通,不存在就不连通。回到该题,我们将互相连接的元素都放入一个集合中,如果发现有一条边的两头结点都是同一个集合的,说明这条边尚未连接时,这两个结点已经连接起来了,现在再加上这条边,这种情况只有可能是环。


提升

  当发现双方所含的集合不一样的时候,选择其中一个集合,另一个集合加入该集合。

这里有其实应该选择将一个小的集合,加入到一个大的集合。 而不是随机的选择,或者固定左集合往右集合合并,这就是路径压缩算法的核心思想。

这篇关于并查表(认老大的区分方式)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/251821

相关文章

gradle第三方Jar包依赖统一管理方式

《gradle第三方Jar包依赖统一管理方式》:本文主要介绍gradle第三方Jar包依赖统一管理方式,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录背景实现1.顶层模块build.gradle添加依赖管理插件2.顶层模块build.gradle添加所有管理依赖包

Linux之systemV共享内存方式

《Linux之systemV共享内存方式》:本文主要介绍Linux之systemV共享内存方式,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录一、工作原理二、系统调用接口1、申请共享内存(一)key的获取(二)共享内存的申请2、将共享内存段连接到进程地址空间3、将

Maven中引入 springboot 相关依赖的方式(最新推荐)

《Maven中引入springboot相关依赖的方式(最新推荐)》:本文主要介绍Maven中引入springboot相关依赖的方式(最新推荐),本文给大家介绍的非常详细,对大家的学习或工作具有... 目录Maven中引入 springboot 相关依赖的方式1. 不使用版本管理(不推荐)2、使用版本管理(推

C#使用StackExchange.Redis实现分布式锁的两种方式介绍

《C#使用StackExchange.Redis实现分布式锁的两种方式介绍》分布式锁在集群的架构中发挥着重要的作用,:本文主要介绍C#使用StackExchange.Redis实现分布式锁的... 目录自定义分布式锁获取锁释放锁自动续期StackExchange.Redis分布式锁获取锁释放锁自动续期分布式

Java对象转换的实现方式汇总

《Java对象转换的实现方式汇总》:本文主要介绍Java对象转换的多种实现方式,本文通过实例代码给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友参考下吧... 目录Java对象转换的多种实现方式1. 手动映射(Manual Mapping)2. Builder模式3. 工具类辅助映

Spring Boot读取配置文件的五种方式小结

《SpringBoot读取配置文件的五种方式小结》SpringBoot提供了灵活多样的方式来读取配置文件,这篇文章为大家介绍了5种常见的读取方式,文中的示例代码简洁易懂,大家可以根据自己的需要进... 目录1. 配置文件位置与加载顺序2. 读取配置文件的方式汇总方式一:使用 @Value 注解读取配置方式二

JAVA保证HashMap线程安全的几种方式

《JAVA保证HashMap线程安全的几种方式》HashMap是线程不安全的,这意味着如果多个线程并发地访问和修改同一个HashMap实例,可能会导致数据不一致和其他线程安全问题,本文主要介绍了JAV... 目录1. 使用 Collections.synchronizedMap2. 使用 Concurren

C# foreach 循环中获取索引的实现方式

《C#foreach循环中获取索引的实现方式》:本文主要介绍C#foreach循环中获取索引的实现方式,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友参考下吧... 目录一、手动维护索引变量二、LINQ Select + 元组解构三、扩展方法封装索引四、使用 for 循环替代

将Java程序打包成EXE文件的实现方式

《将Java程序打包成EXE文件的实现方式》:本文主要介绍将Java程序打包成EXE文件的实现方式,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录如何将Java程序编程打包成EXE文件1.准备Java程序2.生成JAR包3.选择并安装打包工具4.配置Launch4

springboot上传zip包并解压至服务器nginx目录方式

《springboot上传zip包并解压至服务器nginx目录方式》:本文主要介绍springboot上传zip包并解压至服务器nginx目录方式,具有很好的参考价值,希望对大家有所帮助,如有错误... 目录springboot上传zip包并解压至服务器nginx目录1.首先需要引入zip相关jar包2.然