测试下图中是否存在环

python 检查集合内是否存在 并查集 python_python


python代码实现

"""
并查集
"""
##### 查找节点node的根节点函数
def find_root(node, parent):
    if parent[node] == -1:  # node节点的父节点就是它自己时,说明它自己就是根节点
        return node
    else:  # node的父节点不是就一层一层向上找父节点
        return find_root(parent[node], parent)
    
##### 合并两个节点x,y的函数, 该函数返回1表示成功合并,返回0表示合并失败。
# 当合并的两个点正好在同一个集合里就会合并失败
def union(x, y, parent):
    # 分别找x,y节点的根
    x_root, y_root = find_root(x, parent), find_root(y, parent)

    # 两节点的根不同,可合并;两节点的根相同会形成环,不可合并
    if x_root == y_root:
        return 0
    else:
        parent[x_root] = y_root
        return 1


if __name__ == "__main__":
    print("输入节点个数:")
    n  = int(input()) 
    print("输入边条数:")
    line_num = int(input())
    # 6个节点的父节点列表的初始化
    parent = [-1 for i in range(n)]
    
    # parent = [1,4,1,4,-1,-1] 这三行试一试find_root递归写的对不对
    # print(find_root(2, parent))
    # print(find_root(4, parent))

    #### 根据事例中的图写一段测试代码
    edges = []
    for times in range(line_num):
         edges.append([int(i) for i in input().split(' ')])
    # 以上获得所有输入信息
    # 将所有信息添加到图中,合并节点,判断是否形成环
    for line in edges:
        x, y = line[0], line[1]
        ans = union(x, y, parent)
        if ans == 0:
            print("Cycle detected!\n")
            break
    if ans == 1:
        print("No cycle found.\n ")
"""
测试输入
6
6
0 1
1 2
1 3
2 5
2 4
3 4
另一测试例
6
5
0 1
1 2
1 3
2 5
3 4
"""

以上代码可能出现的问题:

在有这种长链时,查找parent会特别的慢

python 检查集合内是否存在 并查集 python_数据结构_02


解决方法:

解决方法:将每个树的深度做一个记录,为了使合并的树越小越好,引入rank列表用来存放每个节点在其所在树的高度位置。我们将高度大的树的根节点作为高度小的树的根节点的父节点。

python 检查集合内是否存在 并查集 python_python_03

"""
并查集
"""
##### 查找节点node的根节点函数
def find_root(node, parent):
    if parent[node] == -1:  # node节点的父节点就是它自己时,说明它自己就是根节点
        return node
    else:  # node的父节点不是就一层一层向上找父节点
        return find_root(parent[node], parent)
    
##### 合并两个节点x,y的函数, 该函数返回1表示成功合并,返回0表示合并失败。
# 当合并的两个点正好在同一个集合里就会合并失败
def union(x, y, parent, rank):
    # 分别找x,y节点的根
    x_root, y_root = find_root(x, parent), find_root(y, parent)

    # 两节点的根不同,可合并;两节点的根相同会形成环,不可合并
    if x_root == y_root:
        return 0
    else:
        # parent[x_root] = y_root 在拼接时不能直接拼接了,要加入rank比较
        if rank[x_root] > rank[y_root]:
            parent[y_root] = x_root
        elif rank[x_root] < rank[y_root]:
            parent[x_root] = y_root
        else:  # 两个相等的情况时,一个作为另一个的父节点,作为父节点的的深度需增加
            parent[x_root] = y_root
            rank[y_root] += 1
        return 1


if __name__ == "__main__":
    print("输入节点个数:")
    n  = int(input()) 
    print("输入边条数:")
    line_num = int(input())
    # 6个节点的父节点列表的初始化
    parent = [-1 for i in range(n)]
    
    # parent = [1,4,1,4,-1,-1] 这三行试一试find_root递归写的对不对
    # print(find_root(2, parent))
    # print(find_root(4, parent))

    # 添加一个rank列表,用来存放每个节点到该节点所在树的根节点的深度,初始化为0
    rank = [0 for i in range(n)]

    #### 根据事例中的图写一段测试代码
    edges = []
    for times in range(line_num):
         edges.append([int(i) for i in input().split(' ')])
    # 以上获得所有输入信息
    # 将所有信息添加到图中,合并节点,判断是否形成环
    for line in edges:
        x, y = line[0], line[1]
        ans = union(x, y, parent, rank)
        if ans == 0:
            print("Cycle detected!\n")
            break
    if ans == 1:
        print("No cycle found.\n ")

"""
测试输入
6
6
0 1
1 2
1 3
2 5
2 4
3 4
另一测试例
6
5
0 1
1 2
1 3
2 5
3 4
"""