【python-NOJ-44】【001】—第五季【类】—朋友圈(并查集)

题目描述:

 解析:

这个问题抽象为求一个图的连通子图的个数。可以采用DFS遍历,求出连通度;或者采用并查集

此题使用使用并查集

(1)初始化:将每个节点的父节点初始化为自己,此时n个节点有n个朋友圈;

(2)第一步:根据输入的序列,将node数组和对应的father数组进行第二波初始化,注意father[tail]=head,这样才能成功的从后往前寻找时停止;

(3)第三步:遍历。对于node中的每个节点,查找其父节点,如果此节点等于其父节点,即father[node]=node,说明已经找到祖先节点,则停止对该节点的查找,跳入下一个节点。反之说明还没有找到其祖先节点,那么使当前节点的父节点等于其父节点的父节点,然后再比较更新过后,这个节点的父节点跟父节点的父节点进行比较,直到相同。

(4)最后,整理为字典得到朋友圈个数。

知识点:

(1)enumerate()函数

——语法:enumerate(sequence, [start=0])

——参数:sequence是一个序列,迭代器或者其他迭代对象;start下标起始位置;

——返回值:返回enumerate枚举对象;

>>>seasons = ['Spring', 'Summer', 'Fall', 'Winter']
>>> list(enumerate(seasons))
[(0, 'Spring'), (1, 'Summer'), (2, 'Fall'), (3, 'Winter')]
>>> list(enumerate(seasons, start=1))       # 下标从 1 开始
[(1, 'Spring'), (2, 'Summer'), (3, 'Fall'), (4, 'Winter')]

(2)并查集

了解并查集的思想和基本操作方式:即一直更新一个节点,第一次是它本身的父节点,第二次它的父节点变为它父节点的父节点,即爷节点。再比较它此时的父节点(也就是爷节点)是否跟爷节点(即太爷节点)相同,相同则break停止对该节点的寻找,不相同再更新其父节点为太爷节点。然后一直如此。

核心代码如下:

for node in nodes:
    while True:
        # 对于每个节点,找它的父节点,看他的父节点的父节点是谁,直到找到父节点跟节点相同,说明到头
        father_of_node=father[node]
        if father_of_node!=father[father_of_node]:
            father[node]=father[father_of_node]
        else:
            break

代码:

n=int(input())
nodes=list(range(n))
father=list(range(n))

m=int(input())
edges=[]
for i in range(m):
    edge=[int(i) for i in input().split(' ')]
    edges.append(edge)
for edge in edges:
    head=edge[0]
    tail=edge[1]
    father[tail]=head
for node in nodes:
    while True:
        # 对于每个节点,找它的父节点,看他的父节点的父节点是谁,直到找到父节点跟节点相同,说明到头
        father_of_node=father[node]
        if father_of_node!=father[father_of_node]:
            father[node]=father[father_of_node]
        else:
            break
dic={}
for i,f in enumerate(father):
    dic[f]=[]
print(len(dic))

延展知识:

此题还可以使用DFS遍历得到。

(1)原理:对于每个节点,对其执行一遍DFS后会找到与之相连的所有节点,并将它标记。所以,执行了多少遍DFS,就有多少个连通子图

(2)实现方法:刚开始每个节点都没走过。对于每个节点,判断其是否走过,即used[i]==1,没有则对齐实行DFS,同时k++,标记实行了几次DFS。对于每次DFS而言,其都通过不断调用自身,找到了与自己相连的所有节点。

基本代码如下:

n=int(input())
dis=[[0 for i in range(n)]for j in range(n)]
used=[0 for i in range(n)]
m=int(input())
for i in range(m):
    a=[int(i) for i in input().split(' ')]
    dis[a[0]][a[1]]=1
    dis[a[1]][a[0]]=1

def DFS(i,n):
    used[i]=1
    for j in range(n):
        if dis[i][j]==1:
            if used[j]!=1:
                DFS(j,n)
k=0
for i in range(n):
    if used[i]==0:
        k+=1
        DFS(i,n)
print(k)
  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值