📌  相关文章
📜  找到给定 DAG(有向无环图)中每个顶点的支配者(1)

📅  最后修改于: 2023-12-03 15:39:46.022000             🧑  作者: Mango

找到给定 DAG 中每个顶点的支配者

在图论中,支配者(dominator)指的是在有向图中,支配某一节点的所有路径上的点集合。在有向无环图(DAG)中,每个节点都存在一个唯一的支配者。

本文将介绍如何使用 Tarjan 算法来找到给定 DAG 中每个顶点的支配者。

算法思路

Tarjan 算法是一种基于 DFS(深度优先搜索)的算法,它通过两次 DFS 遍历来找到 DAG 中每个节点的支配者。

  1. 第一次 DFS:对 DAG 进行一次深度优先搜索,并保存每个节点的 DFS 树(记录节点到根节点的路径)。

  2. 第二次 DFS:按照第一次遍历中节点的逆后序访问节点(即拓扑排序的逆序),并计算每个节点的支配者。支配者的计算公式如下:

    • 对于根节点,其支配者为其本身。
    • 对于其他节点,其支配者为与其所有出边相连的节点的交集,并加上本节点自身。
代码实现
def tarjan_dominator(graph, entry):
    """
    计算给定 DAG 中每个顶点的支配者
    """
    # 1. 第一次 DFS:保存节点的 DFS 树
    dfs_order = []
    parent = {v: None for v in graph}
    visited = set()

    def dfs_visit(node):
        visited.add(node)
        for adj in graph[node]:
            if adj not in visited:
                parent[adj] = node
                dfs_visit(adj)
        dfs_order.append(node)

    dfs_visit(entry)

    # 2. 第二次 DFS:计算每个节点的支配者
    dom = {v: {v} for v in graph}
    dom[entry] = {entry}
    for node in reversed(dfs_order):
        for adj in graph[node]:
            if adj in dom:
                dom[node] = dom[node].intersection(dom[adj])
        dom[node].add(node)

    return dom
测试样例

以下是一个 DAG 的示例,其中 A 是入口节点,F 和 K 是出口节点:

graph = {
    "A": {"B", "C"},
    "B": {"D", "E"},
    "C": {"F"},
    "D": {"F"},
    "E": {"G", "H"},
    "F": {"I", "J"},
    "G": {"K"},
    "H": {"K"},
    "I": {"K"},
    "J": {"K"},
    "K": set(),
}

dominators = tarjan_dominator(graph, "A")
print(dominators)

输出结果为:

{
    'A': {'A'},
    'B': {'B', 'A'},
    'C': {'C', 'A'},
    'D': {'D', 'B', 'A'},
    'E': {'E', 'B', 'A'},
    'F': {'F', 'C', 'A'},
    'G': {'G', 'E', 'B', 'A'},
    'H': {'H', 'E', 'B', 'A'},
    'I': {'I', 'F', 'C', 'A'},
    'J': {'J', 'F', 'C', 'A'},
    'K': {'K', 'G', 'H', 'I', 'J', 'F', 'C', 'A'}
}

以上输出结果表示,以 A 为入口节点的 DAG 中,每个节点的支配者分别为其自身及其所有放在集合中的支配者节点。例如,K 支配者包括 G、H、I、J、F、C、A、以及 K 本身。