并查集-查询与合并

并查集:–使用的数据结构:字典

形式也是通过 key-value来记录关系,key都是不同的,value可以是相同的.
如下: res[0] = 1 , res[1] = 2 , res[2] = 2
在这里插入图片描述

初始化 :把每个点所在集合初始化为其自身。
通常来说,这个步骤在每次使用该数据结构时只需要执行一次,无论何种实现方式,时间复杂度均为O(N)。
查找:查找元素所在的集合,即根节点。
合并:将两个元素所在的集合合并为一个集合。通常来说,合并之前,应先判断两个元素是否属于同一集合,这可用上面的“查找”操作实现

下面一些理解是看labuladong的,很赞!
并查集是用来查询两个元素是否属于同一个集合,并且快速合并两个元素所在的集合。最终判断有多少个不连通的集合,这个过程中,朋友的朋友都是朋友,都算是一个集合之中。

通过集合中的一个元素来代表这个集合,转化为树的结构就是树的根节点。所以集合中每个节点都有一个父亲节点,根节点的父亲节点是他自己。

f = {}
f.setfault(key, value)

字典的setdefault(),如果字典中已经有这个key了,那么是无效的。只有没有这个键的情况下生效。
dict.setdefault(key, default=None)
参数
key – 查找的键值。
default – 键不存在时,设置的默认键值。
返回值
如果字典中包含有给定键,则返回字典中该键对应的值,否则返回为该键设置的值。

实例
dict = {‘runoob’: ‘菜鸟教程’, ‘google’: ‘Google 搜索’}

print “Value : %s” % dict.setdefault(‘runoob’, None)–‘菜鸟教程’
print “Value : %s” % dict.setdefault(‘Taobao’, ‘淘宝’)–‘淘宝’

并查集要解决的问题就是返回连通集的数目,将相互连通的集合进行合并,朋友的朋友也是我的朋友。做法就是如果确定两个节点是连通的,那么不断寻找其根节点,将根节点进行连通,根节点的特点就是其父节点是其本身。

一 模板

有模板可以套:
find函数,找到该节点的根节点;union函数,连接两个节点的根节点。

def UF():
    f = {}  # f存储的键是节点,值是其父节点;根节点的父节点是本身
    s = {} # 记录树的大小,用来平衡,后面有解释。
    count = len(XXX) # count指的是节点的个数,起始假设所有节点都是孤立的,所以有count个连通集

    def find(x):
    	f.setdefault(x, x)
        while f[x] != x:
            x = f[x]
        return x # 根节点

    def union(x, y): # x和y是有连通关系的两个节点
        nonlocal count
        root_x = find(x)
        root_y = find(y)
        if root_x == root_y:
            return
        f[root_x] = root_y
        count -= 1

    # 直到把所有的节点的连通关系都消费完了,就返回count

二 树平衡优化

注意,上述可能出现头重脚轻的情况,因为没有区分两个根节点哪个更合适,就直接把一个根节点当作另一个的根节点。所以加一个判断:把小树接到大树的下面,因为大树接小树只会更长,所以加一个记录大小的字典s。
通过比较大小,可以保证树的生长相对平衡,类似平衡二叉树,高度就在logN,所以时间复杂度就是logN,否则最坏情况下可能是N。
在这里插入图片描述

def Union_Find():
    f = {}  # f存储的键是节点,值是其父节点;根节点的父节点是本身
    s = {}
    count = len(XXX)  # count指的是节点的个数,起始假设所有节点都是孤立的,所以有count个连通集

    def find(x):
    	f.setdefault(x, x)
        while f[x] != x:
            x = f[x]
        return x # 根节点

    def union(x, y): # x和y是有连通关系的两个节点
        nonlocal count
        root_x = find(x)
        root_y = find(y)
        if root_x == root_y:
            return
        if s.setdefault(root_x, 1) < s.setdefault(root_y, 1): 
        # setdefault()如果存在,就返回已经存在的,不存在则返回1. 
        	f[root_x] = root_y
        	s[root_y] += s[root_x] #存储树的大小
        
        else:
        	f[root_y] = root_x
        	s[root_x] += s[root_y]
        	
        count -= 1

    # 直到把所有的节点的连通关系都消费完了,就返回count

三 路径压缩

路径压缩就是把一个节点下面,所带的那些分支都归到这个节点下。这样在寻找根节点的时候,就是O(1)的寻找复杂度。

只需要在搜索根节点的时候顺便加上一句代码,把后面节点的根节点进行前移,这样搜索的时间复杂度大大减小:

    def find(x):
        while f[x] != x:
            f[x] = f[f[x]]
            x = f[x]
        return x # 根节点

这个操作使得往上搜寻的速度大大减少。可以跳过节点,隔着访问。
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
这样就压缩结束。

班上有 N 名学生。其中有些人是朋友,有些则不是。他们的友谊具有是传递性。如果已知 A 是 B 的朋友,B 是 C 的朋友,那么我们可以认为 A 也是 C 的朋友。所谓的朋友圈,是指所有朋友的集合。

给定一个 N * N 的矩阵 M,表示班级中学生之间的朋友关系。如果M[i][j] = 1,表示已知第 i 个和 j 个学生互为朋友关系,否则为不知道。你必须输出所有学生中的已知的朋友圈总数。

示例 1:
输入:
[[1,1,0],
[1,1,0],
[0,0,1]]
输出:2
解释:已知学生 0 和学生 1 互为朋友,他们在一个朋友圈。
第2个学生自己在一个朋友圈。所以返回 2 。


class Solution(object):
    def findCircleNum(self, M):
        """
        :type M: List[List[int]]
        :rtype: int
        """
        count = len(M)
        f = {}
        s = {}

        def Find(x):
            f.setdefault(x, x)
            while f[x] != x:
                f[x] = f[f[x]]
                x = f[x]
            return x
        
        def Union(x, y):
            nonlocal count
            root_x = Find(x)
            root_y = Find(y)

            if root_x == root_y:
                return
        
        # setdefault可以直接避免不存在的问题
            if s.setdefault(root_x, 1) < s.setdefault(root_y, 1):
                f[root_x] = root_y
                s[root_y] += s[root_x] 
            else:
                f[root_y] = root_x
                s[root_x] += s[root_y]
            count -= 1

        for i in range(len(M)):
            for j in range(len(M[0])):
                if M[i][j] == 1:
                    Union(i, j)
                    
        return count
            

本题构造数据是需要n*n的时间复杂度,两个for循环,但是连通的union取决于find,find的时间复杂度是O(1),所以union的时间复杂度也是O(1).

  1. 冗余连接
    在本问题中, 树指的是一个连通且无环的无向图。

输入一个图,该图由一个有着N个节点 (节点值不重复1, 2, …, N) 的树及一条附加的边构成。附加的边的两个顶点包含在1到N中间,这条附加的边不属于树中已存在的边。

结果图是一个以边组成的二维数组。每一个边的元素是一对[u, v] ,满足 u < v,表示连接顶点u 和v的无向图的边。

返回一条可以删去的边,使得结果图是一个有着N个节点的树。如果有多个答案,则返回二维数组中最后出现的边。答案边 [u, v] 应满足相同的格式 u < v。在这里插入图片描述

注意!!!nonlocal只用于某个值,字典不需要!!!

# 并查集 可以联系朋友圈这道题 
# 解题思路是去掉一条边,如果还是一个连通图则成立
class Solution:
    def findRedundantConnection(self, edges: List[List[int]]) -> List[int]:

        def find(x):
            f.setdefault(x, x)
            while f[x] != x:
                f[x] = f[f[x]]
                x = f[x]
            return x

        def Union(x, y, count):
            # nonlocal count
            root_x = find(x)
            root_y = find(y)
            if root_x == root_y:
                return count
            if s.setdefault(root_x, 1) < s.setdefault(root_y, 1):
                # 此处易出错!!!!! 注意是根的根变化,不只是x变化!!!
                f[root_x] = root_y
                s[root_y] += s[root_x]

            else:
                f[root_y] = root_x
                s[root_x] += s[root_y]

            count -= 1
            return count

        for i in range(-1, -len(edges),-1):
            
            f = {}
            s = {}
            count = len(edges)
            list_cp = list(edges)
            list_cp.pop(i)
            for edge in list_cp:
                count = Union(edge[0], edge[1], count)
            if len(f) == len(edges) and count == 1:
                return edges[i]

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值