代码随想录训练营 Day55打卡 图论part05 并查集理论基础 107. 寻找存在的路径

代码随想录训练营 Day55打卡 图论part05

一、并查集理论基础

并查集(Disjoint Set Union,简称 DSU 或 Union-Find)是一种用于处理动态连通性问题的数据结构,常用于解决图的连通性问题,如判断两个节点是否属于同一个连通分量,或者合并两个节点所属的集合。它可以在近乎常数时间内完成查询和合并操作,常用于图论算法中,如 Kruskal 算法求最小生成树。

并查集的核心操作:

  1. 查找(Find):查找元素所属的集合(找到该元素的代表节点)。
  2. 合并(Union):将两个元素所属的集合合并。

基础理论:

  • 每个元素最初在自己独立的集合中,视为一个单节点的树。
  • 当进行合并操作时,我们通过将一棵树的根节点指向另一棵树的根节点来合并集合。
  • 使用路径压缩(Path Compression)可以在查找过程中扁平化树结构,加快后续查找。

Python 实现并查集
下面是并查集的 Python 实现,包含路径压缩和按秩合并的优化:

n = 1005  # n 根据题目中节点数量而定,可以根据实际情况调整
father = list(range(n))  # 初始化父节点数组,每个节点的父节点是自己

# 并查集初始化
def init():
    global father
    father = list(range(n))  # 重新初始化父节点数组

# 并查集里寻根的过程(路径压缩)
def find(u):
    if u == father[u]:
        return u
    father[u] = find(father[u])  # 路径压缩
    return father[u]

# 判断 u 和 v 是否属于同一个集合(是否连通)
def isSame(u, v):
    return find(u) == find(v)

# 将 v -> u 这条边加入并查集
def join(u, v):
    root_u = find(u)  # 寻找 u 的根
    root_v = find(v)  # 寻找 v 的根
    if root_u != root_v:  # 如果根不同,则合并
        father[root_v] = root_u

二、卡码107. 寻找存在的路径

题目描述
给定一个包含 n 个节点的无向图中,节点编号从 1 到 n (含 1 和 n )。
你的任务是判断是否有一条从节点 source 出发到节点 destination 的路径存在。
输入描述
第一行包含两个正整数 N 和 M,N 代表节点的个数,M 代表边的个数。
后续 M 行,每行两个正整数 s 和 t,代表从节点 s 与节点 t 之间有一条边。
最后一行包含两个正整数,代表起始节点 source 和目标节点 destination。
输出描述
输出一个整数,代表是否存在从节点 source 到节点 destination 的路径。如果存在,输出 1;否则,输出 0。
输入示例
5 4
1 2
1 3
2 4
3 4
1 4
输出示例
1
提示信息

本题是典型的 并查集(Union-Find) 问题,要求判断图中两个节点是否在同一个连通分量内,也就是从 source 节点是否能到达 destination 节点。

并查集解法思路

  1. 初始化并查集:每个节点都自成一个集合,初始时父节点为其自身。
  2. 合并集合:通过输入的边,将两个节点所在的集合进行合并(即将这两个节点连接起来)。
  3. 判断连通性:查询 source 和 destination 节点是否在同一个集合(即是否可以互相到达)。

代码实现

class UnionFind:
    def __init__(self, n):
        # 初始化,每个节点的父节点指向自己
        self.father = list(range(n + 1))

    # 寻找节点的根,并进行路径压缩
    def find(self, u):
        if u == self.father[u]:
            return u
        else:
            # 路径压缩,直接将当前节点的父节点指向根
            self.father[u] = self.find(self.father[u])
            return self.father[u]

    # 合并两个节点所在的集合
    def union(self, u, v):
        root_u = self.find(u)
        root_v = self.find(v)
        if root_u != root_v:
            # 将v的根节点连接到u的根节点上
            self.father[root_v] = root_u

    # 判断两个节点是否在同一个集合
    def isConnected(self, u, v):
        return self.find(u) == self.find(v)

# 主函数,处理输入输出
def main():
    # 读取节点和边的数量
    n, m = map(int, input().split())

    # 初始化并查集
    uf = UnionFind(n)

    # 处理每条边
    for _ in range(m):
        s, t = map(int, input().split())
        uf.union(s, t)  # 合并节点s和t所在的集合

    # 读取起始节点source和目标节点destination
    source, destination = map(int, input().split())

    # 判断source和destination是否连通
    if uf.isConnected(source, destination):
        print(1)
    else:
        print(0)

# 调用主函数
if __name__ == "__main__":
    main()

  1. UnionFind 类:

init:初始化时,将每个节点的父节点设为自身,表示每个节点自成一个集合。
find:递归查找某个节点的根节点,并在递归返回时进行路径压缩,将节点直接指向根节点,从而加速后续的查找操作。
union:合并两个节点所在的集合。通过 find 方法找到它们的根节点,如果根节点不同,则将一个根节点连接到另一个根节点。
isConnected:判断两个节点是否属于同一个集合,即它们的根节点是否相同。

  1. main 函数:

读取输入的节点数 n 和边数 m,初始化并查集。
处理每条边,使用 union 方法将相连的节点合并。
最后,使用 isConnected 方法判断 source 和 destination 是否在同一个集合中,输出 1 表示连通,0 表示不连通。

卡码题目链接
题目文章讲解

  • 28
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值