图论刷题:并查集

一、并查集的实现

(就是一个合并建树的过程)

class UnionFind:
    def __init__(self, n):
        # 初始化每个元素的父节点为自身
        self.parent = list(range(n))

    def find(self, x):
        # 查找元素 x 的根节点
        while self.parent[x] != x:
            x = self.parent[x]
        return x

    def union(self, x, y):
        # 合并元素 x 和 y 所在的集合
        root_x = self.find(x)
        root_y = self.find(y)
        if root_x != root_y:
            self.parent[root_x] = root_y


# 初始化并查集,包含 5 个元素
uf = UnionFind(5)
print("初始状态下各元素的父节点:", uf.parent)

# 合并元素 0 和 1
uf.union(0, 1)
print("合并 0 和 1 后各元素的父节点:", uf.parent)

# 合并元素 2 和 3
uf.union(2, 3)
print("合并 2 和 3 后各元素的父节点:", uf.parent)

# 合并元素 0 和 3
uf.union(0, 3)
print("合并 0 和 3 后各元素的父节点:", uf.parent)

# 查找元素 1 的根节点
root_1 = uf.find(1)
print("元素 1 的根节点是:", root_1)

# 查找元素 4 的根节点
root_4 = uf.find(4)
print("元素 4 的根节点是:", root_4)

结果:

初始状态下各元素的父节点: [0, 1, 2, 3, 4]
合并 0 和 1 后各元素的父节点: [1, 1, 2, 3, 4]
合并 2 和 3 后各元素的父节点: [1, 1, 3, 3, 4]
合并 0 和 3 后各元素的父节点: [1, 3, 3, 3, 4]
元素 1 的根节点是: 3
元素 4 的根节点是: 4

二、带秩和计数的并查集

class UnionFind:
    def __init__(self, n):
        self.parent = list(range(n))  # 父节点指针
        self.rank = [1] * n           # 子树高度(初始为1)
        self.count = n                # 连通分量计数
    
    def find(self, x):
        # 路径压缩:将查询路径上的节点直接挂到根节点
        if self.parent[x] != x:
            self.parent[x] = self.find(self.parent[x])
        return self.parent[x]
    
    def union(self, x, y):
        # 合并操作:按秩合并避免树过高
        rootX = self.find(x)
        rootY = self.find(y)
        
        if rootX == rootY:
            return False  # 已在同一集合
        
        if self.rank[rootX] < self.rank[rootY]:
            self.parent[rootX] = rootY
        else:
            self.parent[rootY] = rootX
            if self.rank[rootX] == self.rank[rootY]:
                self.rank[rootX] += 1
        self.count -= 1
        return True
    
    def is_connected(self, x, y):
        return self.find(x) == self.find(y)


n = int(input())
v = [[] for _ in range(n)]
for _ in range(n):
    u,k = map(int,input().split())
    v[u].append(k)

uf = UnionFind(n)
for i,vv in enumerate(v):
    for vvele in vv:
        uf.union(i,vvele)

三、举例

# 使用之前提供的模板
edges = [[1,2],[2,3],[3,4],[1,4],[1,5]]
uf = UnionFind(5)  # 节点编号1-5

for u, v in edges:
    print(f"处理边 {u}-{v} 前:", uf.parent[1:])
    if not uf.union(u-1, v-1):  # 注意调整为0-based索引
        print(f"发现冗余边:{u}-{v}")
    print(f"处理后:", uf.parent[1:], "\n")
检测无向图环路(如LeetCode 684)
def findRedundantConnection(edges):
    uf = UnionFind(len(edges)+1)  # 节点编号从1开始
    for u, v in edges:
        if not uf.union(u, v):    # 合并失败说明存在环,即已经连通后再次想要连通,则说明有环
            return [u, v]
    return []
计算连通分量数(如LeetCode 547)
def findCircleNum(isConnected):
    n = len(isConnected)  #isConnected是一个n*n的矩阵,1表示i,j之间有连通,0未没有
    uf = UnionFind(n)
    for i in range(n):
        for j in range(i+1, n): #因为无向图是对称矩阵,所以一半就行
            if isConnected[i][j]:
                uf.union(i, j)
    return uf.count  # 直接返回连通分量总数
Kruskal最小生成树算法
def kruskal(n, edges):
    edges.sort(key=lambda x: x[2])  # 按权重排序
"""
sort 方法:它是 Python 列表对象的一个方法,作用是对列表进行原地排序,
也就是直接改变原列表中元素的顺序。
key=lambda x:x[2]:key 参数用于指定一个函数,这个函数会作用于列表中的每个元素,然后根据函数的返回值来进行排序。
lambda x:x[2] 是一个匿名函数,它接收一个参数 x,并返回 x 的第三个元素(在 Python 里,索引从 0 开始,所以 x[2] 表示第三个元素)。
也就是说,这行代码会按照 edges 列表中每个元素的第三个元素的值来对列表进行排序。
"""
    uf = UnionFind(n)
    mst = []
    for u, v, w in edges:
        if uf.union(u, v):        # 若两节点未连通(不能有环)
            mst.append((u, v, w))
            if len(mst) == n-1:   # 树边数达标
                break
    return mst

四、和DFS\BFS对比

并查集 DFS/BFS
适用场景 动态连通性问题 静态图遍历
时间复杂度 O(α(n)) 近乎常数 O(V+E)
存储方式 隐式树结构 显式邻接表/矩阵
优势操作 实时合并与查询 路径搜索与拓扑排序

你可能感兴趣的:(图论,python,开发语言)