代码随想录训练营 Day55打卡 图论part05
一、并查集理论基础
并查集(Disjoint Set Union,简称 DSU 或 Union-Find)是一种用于处理动态连通性问题的数据结构,常用于解决图的连通性问题,如判断两个节点是否属于同一个连通分量,或者合并两个节点所属的集合。它可以在近乎常数时间内完成查询和合并操作,常用于图论算法中,如 Kruskal 算法求最小生成树。
并查集的核心操作:
- 查找(Find):查找元素所属的集合(找到该元素的代表节点)。
- 合并(Union):将两个元素所属的集合合并。
基础理论:
- 每个元素最初在自己独立的集合中,视为一个单节点的树。
- 当进行合并操作时,我们通过将一棵树的根节点指向另一棵树的根节点来合并集合。
- 使用路径压缩(Path Compression)可以在查找过程中扁平化树结构,加快后续查找。
Python 实现并查集
下面是并查集的 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 节点。
并查集解法思路:
- 初始化并查集:每个节点都自成一个集合,初始时父节点为其自身。
- 合并集合:通过输入的边,将两个节点所在的集合进行合并(即将这两个节点连接起来)。
- 判断连通性:查询 source 和 destination 节点是否在同一个集合(即是否可以互相到达)。
代码实现
python
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()
- UnionFind 类:
init :初始化时,将每个节点的父节点设为自身,表示每个节点自成一个集合。
find :递归查找某个节点的根节点,并在递归返回时进行路径压缩,将节点直接指向根节点,从而加速后续的查找操作。
union :合并两个节点所在的集合。通过 find 方法找到它们的根节点,如果根节点不同,则将一个根节点连接到另一个根节点。
isConnected:判断两个节点是否属于同一个集合,即它们的根节点是否相同。
- main 函数:
读取输入的节点数 n 和边数 m,初始化并查集。
处理每条边,使用 union 方法将相连的节点合并。
最后,使用 isConnected 方法判断 source 和 destination 是否在同一个集合中,输出 1 表示连通,0 表示不连通。