Python之并查集 洛谷 蓝桥杯

简介: Python之并查集 洛谷 蓝桥杯

同时正在备战蓝桥杯 题解如有不足请多批评指正  


大一双非本科在读


目标是进大厂


洛谷:亲戚关系 题目链接


1f7b1f7ea27a4b5d8c4a71ca27e786c0.png


问题分析:这是一道考察并查集的经典例题。


何为并查集?并查集是一种(树型)数据结构 ,用于处理一些不相交集合的合并及查询问题。


思想:用一个数组表示了整片森林,树的根节点唯一标识了一个集合,我们只要找到了某个元素的的树根,就能确定它在哪个集合里。


例如给出数组parent=[0,1,5,1,3,1],parent[i]代表结点i的父结点(很形象吧!?)


观察数组,我们知道:parent[2]=5 ,说明了结点2的父节点是5


再来:parent[5]=1,说明了结点5的父节点是1


再来:parent[1]=1,说明了结点1的父节点是1


对上面这行可能有疑惑:我们先记住,若parent[i]=i  则i是一个集合内的根节点


那么他有什么作用:如果一个结点j 它的父节点的父节点的父节点的父节点.....是i


说明了j和它的父节点和它的父节点的父节点和....一共这么多结点,对于每一个结点,都可以访问到i结点。我们不如把它叫做祖先(能够访问 类似于 含有血缘关系,你和你的祖先,你的父亲的祖先,你的父亲的父亲的祖先都有血缘关系。)


因此这么多结点就构成了一个集合(代表集合的对象是根节点(祖先))


所以对于刚刚提到的数组,由于结点5可以访问到祖先>>1,所以5和1有血缘关系,即5属于祖先1代表的集合,由于结点2的父节点是5,所以它可以通过父节点5访问到祖先>>1,所以2和1有血缘关系,即2属于祖先1代表的集合。


因此我们要判断两个结点是否属于同一个集合,可以借助访问各自的祖先加以判断。


如果祖先相同,那么属于同一集合,否则不属于同一集合。


对于上述数组结点0和结点1就不属于同一集合


所以下面给出访问祖先的代码:(关于优化下面会阐述)

def find_root(x):
    while x!=parent[x]:3如果不是祖先 就访问自己的父节点
        x=parent[x]
    return x

那么上面这段代码就是代表了并查集的思想之一:查询


那么下面我们研究并查集的另一个思想:合并


再次给出上面那个例子parent=[0,1,5,1,3,1]


我们知道结点1,2,3,4,5同属于一个集合(用结点1来标识)


由于parent[0]=0 说明0是一个集合的祖先,这个集合用结点0来标识


由于以结点0为代表的集合元素个数为1,比较特殊,我们不妨给他(集合)添加两个结点


分别是结点6,结点7,因此parent=[0,1,5,1,3,1,0,0]


所以现在 结点0,6,7同属于一个集合(用结点0来标识)


下面研究合并,何为合并?就是将两个集合变为为一个集合


容易想到,我们可以把结点0(祖先)作为结点1的子结点,换句话说,把结点1作为结点0的父节点。


那么这样子有什么用:我们知道用结点0来标识的集合中的结点,都可以访问到结点0,现在结点0又可以访问到结点1,所以用结点0来标识的集合中的结点,都可以访问到结点1,因此用结点1来标识的集合 结点数目扩大了! 这不正达到了我们想要的效果吗?


简言之 集合A和集合B原本分属两大家族,把A的祖先作为B祖先的孩子,因而集合A和B中的所有结点都有了血缘关系,实现了家族合并。


所以下面给出合并的代码:

def union(x,y):#合并
    x_root,y_root=find(x),find(y)
    parent[x_root]=y_root#一般的x_root!=y_root,直接合并过去
                            #如果x_root=y_root,合并本身,没有发生任何变化

所以 代码就可写出了:

def find_root(x):
    while x!=parent[x]:
        x=parent[x]
    return x
def union(x,y):
    x_root=find_root(x)
    y_root=find_root(y)
    parent[x_root]=y_root
n,m,p=map(int,input().strip().split())
parent=[i for i in range(n+1)]#初始化
for i in range(m):#h合并
    tmp=list(map(int,input().strip().split()))
    union(tmp[0],tmp[1])
for j in range(n):#判断
    tmp=list(map(int,input().strip().split()))
    if find_root(tmp[0])==find_root(tmp[1]):
        print('Yes')
    else:
        print('No')

07e8b9fb81a548e8be2cdcd5c6ad545e.png

但是这样超时 所以需要进行优化:


先分析超时的原因:还是利用上面给出的数组parent=[0,1,5,1,3,1,0,0](未合并)


我们可以画出下面这样的关系图:


433a9753b9a540c991ca493810e538e5.jpg


所以科学家们给出了一种方法:路径压缩。


简言之,对于上图,比如在访问4的根节点的时候,经过图中标识的''很长''一段路径,这一段路径由许许多多的结点构成,它们有一个共同特点就是根节点都是1,那么路径压缩要做的就是把这条路径上的所有结点的父节点设为结点1


这样子的作用:比如我下一次查询结点3的根节点的时候,只需要1次,原来需要9999次


def find_root(x):#返回根节点
    if x!=parent[x]:#只要不是根节点
        parent[x]=find_root(parent[x])#修改父节点为根结点
    return parent[x]#返回根节点(父节点)


ed16097cbe8d41da921d76c31e9b1fb5.png


第一次接触路径压缩如果不太好理解,可以先和小郑一样把这个模板记下来~

n,m,p=map(int,input().strip().split())
parent=[i for i in range(n+1)]
def find_root(x):#返回根节点
    if x!=parent[x]:#只要不是根节点
        parent[x]=find_root(parent[x])#把x的父节点设为根节点
    return parent[x]
def union(x,y):
    x_root=find_root(x)
    y_root=find_root(y)
    parent[x_root]=y_root
for i in range(m):
    tmp=list(map(int,input().strip().split()))
    union(tmp[0],tmp[1])
for j in range(p):
    tmp=list(map(int,input().strip().split()))
    if find_root(tmp[0])==find_root(tmp[1]):
        print('Yes')
    else:
        print('No')

5637da3b8be142de8bd130aaba6983e6.png

可见路径压缩帮助我们优化了算法


蓝桥杯真题实战:七段码(第十一届试题E填空压轴)>>考察并查集


46653d1d751f493e8643e18a276aee60.png


代码设计分析:题目就是在问一个连通性问题,从7条边选几个出来判断是不是连通图。


也就是判断是不是属于同一个集合的问题。首先明确一点,并查集两大功能:查找与合并。要先合并,再查找(没有合并一个集合那拿什么来查找?)。所以,相当于我们要先构建亲戚关系网,最后判断所有结点的祖先是否都是同一个,如果是则连通,否则不连通。


那么构建亲戚关系的条件:如果两个顶点直接相连,那么把那个点所属的集合与另外一个点所属的集合合并。当所有的点合并完了,只需要遍历所有的点的祖先,判断是否都是同一个,如果是,大功告成,count加1


def find_root(x):
    if x!=parent[x]:
        parent[x]=find_root(parent[x])
    return parent[x]
def union(x,y):
    x_root,y_root=find_root(x),find_root(y)
    if x_root!=y_root:
        parent[x_root]=y_root
edge=[[0]*7 for i in range(7)]#设一个0号进去,无边与其相连
l=[0,1,2,3,4,5,6]
edge[0][1]=edge[0][2]=edge[1][3]=edge[1][4]=edge[2][3]=1
edge[3][4]=edge[3][5]=edge[4][6]=edge[5][6]=edge[2][5]=1#有边相连记作1
for i in range(7):
    for j in range(7):
        edge[i][j]=max(edge[i][j],edge[j][i])#无向图对称
import itertools
count=0
for i in range(1,8):#灯泡数目
    for j in itertools.combinations(l,i):#比如[(1,2,3),[1,2,4]]
        parent=[i for i in range(7)]
        #判断两两是否连通
        for k in range(0,i):
            for p in range(k+1,i):
                if edge[j[k]][j[p]]==1:#如果连通,合并
                    union(j[k],j[p])
        for m in range(i-1):#判断属于是否同一集合(最终所有的)
            if find_root(j[m])!=find_root(j[m+1]):
                break
        else:
            print(j)#显示符合条件的数据,可删去
            count+=1
print(count)
#答案80

我是小郑 正在奔赴热爱 奔赴山海!

相关文章
|
2月前
|
算法 程序员 图形学
脑洞大开!Python并查集:用最简单的方式,解决最复杂的数据结构问题!
【7月更文挑战第17天】并查集,数据结构明星,处理不相交集合合并与查询。Python实现核心操作:查找与合并。路径压缩优化查找,按秩合并保持平衡。实战应用如图连通性判断,算法竞赛利器。掌握并查集,解锁复杂问题简单解法,照亮编程之旅!
43 10
|
2月前
|
Python
告别低效!Python并查集:数据结构界的超级英雄,拯救你的编程人生!
【7月更文挑战第18天】并查集,数据结构超级英雄,用于不相交集合的合并与查询。Python实现包括初始化、查找根节点和合并操作。应用广泛,如社交网络分析、图论问题、集合划分等。示例代码展示了解决岛屿数量问题,统计连通的“1”单元格数。掌握并查集,提升编程效率,解决复杂问题。
42 6
|
2月前
|
存储 Python
震惊!Python并查集:解锁数据结构新姿势,让你从菜鸟秒变大神!
【7月更文挑战第18天】并查集,一种处理不相交集合的树形数据结构,支持Union(合并)和Find(查询)操作。Python实现中,用字典存储元素及其父节点,初始时每个元素为根。通过路径压缩提高效率。应用包括网络连通性判断、动态连通性检测和集合操作。掌握并查集,提升编程技能,解决复杂问题。开始探索,成为数据结构大师!
31 5
|
2月前
|
存储 算法 索引
深度挖掘:Python并查集背后的秘密,让你的代码逻辑清晰如水晶!
【7月更文挑战第17天】并查集,一种高效处理集合合并与查询的数据结构,常用于图论、社交网络分析等。Python中的实现利用数组存储元素的父节点,通过路径压缩和按秩合并优化查找和合并操作。简单代码示例展示了查找和合并方法,以及应用在检测无向图环路。并查集以其优雅的解决方案在算法世界中闪耀,提升代码的清晰度和效率。
42 5
|
2月前
|
Python
逆天改命!掌握Python并查集,数据结构难题从此不再是你的痛!
【7月更文挑战第18天】并查集,一种神器数据结构,用于处理不相交集合合并与查询,解决网络连通性等难题。Python实现常通过记录元素父节点
31 4
|
2月前
|
算法 数据挖掘 计算机视觉
Python并查集实战宝典:从入门到精通,让你的数据结构技能无懈可击!
【7月更文挑战第17天】并查集,如同瑞士军刀,是解决元素分组问题的利器,应用于好友关系、像素聚类、碰撞检测和连通性分析等场景。本文从基础到实战,介绍并查集的初始化、查找与路径压缩、按秩合并,以及在Kruskal算法中的应用。通过并查集,实现高效动态集合操作,对比哈希表和平衡树,其在合并与查找上的性能尤为突出。学习并查集,提升算法解决复杂问题的能力。
62 5
|
2月前
|
存储 算法 程序员
庆祝吧!掌握Python并查集,数据结构难题将不再是你的拦路虎!
【7月更文挑战第17天】并查集,一种数据结构,用于不相交集合的合并与查询,尤其适合解决图的连通性问题。通过Python实现,使用列表存储元素的父节点以判断集合关系。基本操作包括查找(确定元素集合)和合并(组合集合)。示例展示了如何用并查集配合Kruskal算法构建最小生成树。掌握并查集能高效处理复杂问题,优化后的查找和合并操作接近O(1)复杂度,是解决算法挑战的利器。
33 4
|
2月前
|
算法 计算机视觉 Python
Python并查集大揭秘:让你在算法界呼风唤雨,秒杀一切复杂场景!
【7月更文挑战第18天】并查集是Python中解决集合动态合并与查询的利器,常用于复杂问题。例如,在社交网络中快速判断用户是否在同一朋友圈,通过路径压缩优化的`UnionFind`类实现。另外,计算图像中岛屿数量也可借助并查集,将相邻像素合并成集合。并查集的应用显示了其在算法中的高效和灵活性,是提升编程技能的关键工具。
31 2
|
2月前
|
存储 算法 Python
火箭般的提升!学会Python并查集,让你的算法能力飞跃新高度!
【7月更文挑战第17天】并查集,高效解决集合合并查询问题,常用于图的连通性判断。Python实现关键包含查找和合并操作。初始化时,元素各自为集合。查找使用路径压缩优化,合并则可选按秩策略保持平衡。例如,检测无向图环路,遍历边,若并查集发现边两端已在同一集合,则存在环。掌握并查集,提升算法能力,助你在问题解决中一飞冲天!动手实践,成为算法达人!
40 2
|
2月前
|
算法 计算机视觉 开发者
燃爆全场!Python并查集:数据结构界的网红,让你的代码炫酷无比!
【7月更文挑战第16天】并查集,Python中的效率明星,处理不相交集合合并与查询。用于社交网络分析、图像处理、图论算法等领域。优雅实现结合路径压缩和按秩合并
28 1