Python中使用DFS和Tarjan算法判断图(Graph)是否有割点(Articulation Point)

2023-04-11 00:00:00 算法 判断 DFS

首先,我们需要了解图中的割点(Articulation Point)是什么。割点可以理解为去掉一个节点会导致图不连通,即这个节点连接了整张图,也是连接其他连通分量的关键节点。

接着,我们分别用DFS和Tarjan算法来判断图是否有割点。

DFS算法:

def dfs(vertex, visited, low_time, dfn_time, parent, is_articulation):
    visited.add(vertex)
    dfn_time[vertex] = low_time[vertex] = dfs.time  # 前向星时间戳
    dfs.time += 1
    child_num = 0  # 子节点数量

    for w in graph[vertex]:
        if w not in visited:
            child_num += 1
            parent[w] = vertex
            dfs(w, visited, low_time, dfn_time, parent, is_articulation)
            low_time[vertex] = min(low_time[vertex], low_time[w])
            if parent[vertex] == -1 and child_num > 1:
                is_articulation[vertex] = True
            elif parent[vertex] != -1 and dfn_time[vertex] <= low_time[w]:
                is_articulation[vertex] = True
        elif w != parent[vertex]:
            low_time[vertex] = min(low_time[vertex], dfn_time[w])


def find_articulation_points():
    visited = set()  # 已经访问过的节点
    low_time = [-1] * n  # 各节点最早能访问到的时间戳
    dfn_time = [-1] * n  # 最早遍历到各节点的时间戳
    parent = [-1] * n  # 各节点的父节点
    is_articulation = [False] * n  # 是否为割点
    dfs.time = 0

    for i in range(n):
        if i not in visited:
            dfs(i, visited, low_time, dfn_time, parent, is_articulation)

    for i in range(n):
        if is_articulation[i]:
            print("Node", i, "is an articulation point.")


n = 8
graph = [[1, 2, 3], [0, 2], [0, 1], [0, 4], [3, 5], [4, 6, 7], [5], [5]]
find_articulation_points()

Tarjan算法:

def tarjan(vertex, visited, low_time, dfn_time, parent, is_articulation):
    visited.add(vertex)
    low_time[vertex] = dfn_time[vertex] = tarjan.time  # 时间戳
    tarjan.stack.append(vertex)

    for w in graph[vertex]:
        if w not in visited:
            parent[w] = vertex
            tarjan(w, visited, low_time, dfn_time, parent, is_articulation)
            low_time[vertex] = min(low_time[vertex], low_time[w])
            if dfn_time[vertex] <= low_time[w]:
                is_articulation[vertex] = True
                while tarjan.stack[-1] != w:
                    u = tarjan.stack.pop()
                    is_articulation[u] = True
                tarjan.stack.pop()
        elif w != parent[vertex]:
            low_time[vertex] = min(low_time[vertex], dfn_time[w])

    if low_time[vertex] == dfn_time[vertex]:
        while len(tarjan.stack) != 0 and tarjan.stack[-1] != vertex:
            u = tarjan.stack.pop()
            print(u)
        u = tarjan.stack.pop()
        print(u)


def find_articulation_points():
    visited = set()  # 已经访问过的节点
    low_time = [-1] * n  # 各节点最早能访问到的时间戳
    dfn_time = [-1] * n  # 最早遍历到各节点的时间戳
    parent = [-1] * n  # 各节点的父节点
    is_articulation = [False] * n  # 是否为割点
    tarjan.time = 0
    tarjan.stack = []

    for i in range(n):
        if i not in visited:
            tarjan(i, visited, low_time, dfn_time, parent, is_articulation)

    for i in range(n):
        if is_articulation[i]:
            print("Node", i, "is an articulation point.")


n = 8
graph = [[1, 2, 3], [0, 2], [0, 1], [0, 4], [3, 5], [4, 6, 7], [5], [5]]
find_articulation_points()

上述两种算法都可以用来判断图中是否有割点。其中,DFS算法实现起来比较简单,而Tarjan算法的实现较为复杂。两种算法的时间复杂度均为$O(V+E)$。

相关文章