代码随想录算法训练营 Day 48

1 minute read

Published:

并查集

并查集常用来解决连通性问题,主要的两个功能:1. 将两个元素添加到一个集合中;2. 判断两个元素在不在同一个集合中

Q1. KamaCoder 108

当两个节点的根节点一致时,说明他们之间已经有一条路径。如果此时再加上一条边直接连接这两个节点,就会形成环。因此我们首先判断要插入边的两个节点是否有同一个根节点,没有的话通过并查集将其加入到同一个集合中

class UnionFind:
    def __init__(self, n):
        self.parent = list(range(n + 1))
    
    def find(self, u):
        if u != self.parent[u]:
            self.parent[u] = self.find(self.parent[u])
        
        return self.parent[u]
    
    def union(self, u, v):
        root_u = self.find(u)
        root_v = self.find(v)
        if root_u != root_v:
            self.parent[root_v] = root_u
    
    def is_same(self, u, v):
        return self.find(u) == self.find(v)

if __name__ == '__main__':
    n = int(input())
    
    uf = UnionFind(n)
    
    for _ in range(n):
        s, t = map(int, input().split())
        if uf.is_same(s, t):
            print(s, t)
            exit()
        uf.union(s, t)

Q2. KamaCoder 109

有向图要构成树有两个条件:1. 除根节点外,每个节点入度必须为1;2. 不可以形成有向环

具体的步骤:1. 是否有造成入度为2的边;2. 如果有的话,逐一判断不包括一条边时,能够形成树,如果可以的话说明这条边就是要删除的边;3. 如果没有入度为2的节点,说明此时存在有向环,删除环中一条边即可

class UnionFind:
    def __init__(self, n):
        self.parent = list(range(n + 1))
        
    
    def find(self, u):
        if u != self.parent[u]:
            self.parent[u] = self.find(self.parent[u])
        
        return self.parent[u]
    
    def union(self, u, v):
        root_u = self.find(u)
        root_v = self.find(v)
        
        if root_u != root_v:
            self.parent[root_v] = root_u
    
    def is_same(self, u, v):
        return self.find(u) == self.find(v)

def find_cycle(size, edges):
    uf = UnionFind(size)
    for x, y in edges:
        if uf.is_same(x, y):
            return x, y
        uf.union(x, y)
        
    return None

def is_tree_after_remove(size, e, edges):
    uf = UnionFind(size)
    for x, y in edges:
        if x == e[0] and y == e[1]:
            continue
        if uf.is_same(x, y):
            return False
        uf.union(x, y)
        
    return True
    

if __name__ == '__main__':
    n = int(input())
    
    in_degree = [0] * (n + 1)
    edges = []
    
    for _ in range(n):
        s, t = map(int, input().split())
        in_degree[t] += 1
        edges.append((s, t))
    
    edge = []
    for i in range(n - 1, -1, -1):
        if in_degree[edges[i][1]] == 2:
            edge.append(edges[i])
    
    if edge:
        for e in edge:
            if is_tree_after_remove(n, e, edges):
                print(e[0], e[1])
                exit()
    else:
        x, y = find_cycle(n, edges)
        print(x, y)