并查集解决无向图的连通性问题


最近遇到一道无向图的连通性相关的问题。一般解决图相关的问题,最直接的思路就是利用dfs暴力搜索,因为图可以看成是一种特殊的树。但是在查看题解的时候,发现了另一种巧妙的解法。正是利用上一篇所讲到的并查集来优化暴力的dfs算法。下面看具体题目。

问题描述
  小明的实验室有N台电脑,编号1~N。原本这N台电脑之间有N-1条数据链接相连,恰好构成一个树形网络。在树形网络上,任意两台电脑之间有唯一的路径相连。
  不过在最近一次维护网络时,管理员误操作使得某两台电脑之间增加了一条数据链接,于是网络中出现了环路。环路上的电脑由于两两之间不再是只有一条路径,使得这些电脑上的数据传输出现了BUG。
  为了恢复正常传输。小明需要找到所有在环路上的电脑,你能帮助他吗?
输入格式
  第一行包含一个整数N。
  以下N行每行两个整数a和b,表示a和b之间有一条数据链接相连。
  对于30%的数据,1 <= N <= 1000
  对于100%的数据, 1 <= N <= 100000, 1 <= a, b <= N
  输入保证合法。
输出格式
  按从小到大的顺序输出在环路上的电脑的编号,中间由一个空格分隔。
样例输入
5
1 2
3 1
2 4
2 5
5 3
样例输出
1 2 3 5

思路

并查集的原理很简单,具体可以看上一篇文章,这里就不再赘述了。我们重点分析为什么并查集可以解决这个问题。

当并查集录入图的每一条边的时候,都会将这个边的两个节点的根更新一次;又由于这是一张无向连通图,因此所有节点之间都是相互连同的,即最后的根只会有一个值。当新加入的一条边的两个节点的根值相等的时候,就说明这两个节点都曾经作为某条边的端点加入过并查集里了。即 即使不加入这条边,这两个节点之间仍然是连同的。此时再加入一条边,就说明有两条路径可以从a点到b点,即出现了环。下面给出具体的代码。

代码实现

maxn = 100000+5
par = [0]*maxn # 并查集
vis = [0]*maxn # 记录是否遍历过
ret = [0]*maxn # 结果集
edge = [[] for _ in range(maxn)] # 存储每个节点相连的边
res = ''

def find_root(v):
    if par[v] != v:
        par[v] = find_root(par[v])
    return par[v]

def dfs(u, idx, f): # u:当前节点 idx: 结果计数 f:终点
    global ret, res 
    ret[idx] = u # 当前节点加入结果集
    if u == f: # 当前节点就是终点 环遍历完成 输出答案
        ret = ret[:idx+1]
        ret.sort()
        for i in range(idx+1):
            res = res + str(ret[i]) + (' ' if i!=idx else '\n')
           return
    vis[u] = 1 # 还没到重点 标记当前节点
    for i in range(len(edge[u])): # 遍历当前节点的所有边
        v = edge[u][i]
        if not vis[v]: # 这条边没有遍历过
                dfs(v, idx+1, f)
    vis[u] = 0 # 回溯

if __name__ == "__main__":
    n = int(input())
    for i in range(1, n+1):
        par[i] = i
    for i in range(n):
        tmp = input().split()
        u, v = int(tmp[0]), int(tmp[1])
        root_u = find_root(u) # 并查集 搜索根
        root_v = find_root(v)
        if root_u == root_v: # 两节点根相同
            s = u # 搜索起点
            f = v # 搜索终点
        else: # 不相同 进行合并操作
            par[root_u] = root_v
            edge[u].append(v)
            edge[v].append(u)
    dfs(s, 0, f)
    print(res)

小结

通过这道题,让我对并查集的理解又加深了一个层次,并且让我对图的连通性有了新的理解。因此我很好奇并查集到底还有哪些常见的用处。

经过一番资料查找,总结如下:

  1. 基本作用 多个不相交集合

  2. 无向图的连通性(两点之间是否连通)

  3. 判断无向图中的环

  4. 最小生成树的Kruskal 算法


评论
 上一篇
write-up 蓝桥杯2017省赛真题 write-up 蓝桥杯2017省赛真题
蓝桥杯2017省赛真题最近做了一下蓝桥杯2017年省赛的真题,越发让我感到自己水平低下🙃,看来上次三等奖名副其实。差不多一半的题目没有很好的思路,甚至有些题在看题解时又双触及到了知识盲区。对比前几年的情况,17年蓝桥杯感觉略有难度(暴力杯
2020-03-19
下一篇 
对局匹配 对局匹配
对局匹配最近遇到这样一道题目: 小明喜欢在一个围棋网站上找别人在线对弈。这个网站上所有注册用户都有一个积分,代表他的围棋水平。 小明发现网站的自动对局系统在匹配对手时,只会将积分差恰好是K的两名用户匹配在一起。如果两人分差小于或大于K,系统
2020-03-06
  目录