Algorithm DAG-确保有单个源和单个汇的算法

Algorithm DAG-确保有单个源和单个汇的算法,algorithm,graph-theory,directed-acyclic-graphs,Algorithm,Graph Theory,Directed Acyclic Graphs,我必须确保应用程序中的图形是具有唯一源和唯一汇的DAG 具体地说,我必须确保对于给定的开始节点和结束节点(两者在开始时都是已知的),图中的每个节点都位于从开始节点到结束节点的路径上 我已经有了一个Tarjan算法的实现,我用它来识别循环,还有一个拓扑排序算法,一旦Tarjan算法报告图形是DAG,我就可以运行它 确保图形符合此标准的最有效方法是什么?如果图形由邻接矩阵表示,则如果矩阵的第x列为0,则节点x为源节点;如果矩阵的第x行为0,则节点x为汇节点。您可以在矩阵上运行两次快速传递来计算0的行

我必须确保应用程序中的图形是具有唯一源和唯一汇的DAG

具体地说,我必须确保对于给定的开始节点和结束节点(两者在开始时都是已知的),图中的每个节点都位于从开始节点到结束节点的路径上

我已经有了一个Tarjan算法的实现,我用它来识别循环,还有一个拓扑排序算法,一旦Tarjan算法报告图形是DAG,我就可以运行它


确保图形符合此标准的最有效方法是什么?

如果图形由邻接矩阵表示,则如果矩阵的第x列为0,则节点x为源节点;如果矩阵的第x行为0,则节点x为汇节点。您可以在矩阵上运行两次快速传递来计算0的行数和列数,以确定存在多少个源和汇以及它们是什么。这需要时间O(n2),可能是检查这一点的最快方法


如果图形由邻接列表表示,则可以通过检查任何节点是否没有传出边来查找时间O(n)中的所有接收节点。您可以通过为每个节点维护一个布尔值来查找所有汇,该布尔值指示它是否有任何传入边(最初为false)。然后,您可以在时间O(n+m)内遍历列表中的所有边,用传入边标记所有节点。未标记为具有传入边的节点即为源。这个过程需要时间O(m+n),开销很小,可能是最快的方法之一


希望这有帮助

简单的广度或深度优先搜索应该满足这一要求。首先,您可以保留一组节点,这些节点包括您看到的接收器节点。其次,您可以保留一组使用BFS/DFS发现的节点。如果只有一个连接的组件,那么图将被连接。假设您正在为图形使用某种邻接列表样式表示,该算法将如下所示:

create an empty queue
create an empty set to store seen vertices
create an empty set for sink nodes

add source node to queue

while queue is not empty
    get next vertex from queue, add vertex to seen vertices
    if num of adjacent nodes == 0
        add sink nodes to sink node set
    else
        for each node in adjacent nodes
        if node is not in seen vertices
            add node to queue

return size of sink nodes == 1 && size of seen vertices == total number in graph
这将是图中顶点和边的线性数量


请注意,只要知道要从哪个源顶点开始,这也将确保单个源的属性:BFS/DFS不会发现作为源的任何其他顶点,因此看到的顶点的大小不会是图形中的总数。

如果算法将弱连接的DAG作为输入,假设只有一个节点s的入度为零,只有一个节点t的出度为零,而所有其他节点都有正的入度和出度,那么s可以到达所有其他节点,所有其他节点都可以到达t。自相矛盾的是,假设存在一个s无法到达的节点v。因为没有节点可以到达s,也就是说,v也不能到达s。因此,v和s是断开的,这与假设相矛盾。另一方面,如果DAG不是弱连接的,它肯定不能满足您想要的要求。总之,您可以首先简单地使用BFS/DFS计算DAG的弱连接组件,同时记住其入度或出度为零的节点数。这个算法的复杂度是O(| E |)。

首先,从源节点开始对图进行DFS,正如您所说,源节点是预先知道的。如果遇到后缘[1],则有一个循环,您可以退出并出现错误。在此遍历过程中,您可以确定是否存在无法从源[2]访问的节点,并采取适当的操作

确定图形为DAG后,可以确保每个节点位于另一个DFS从源到接收器的路径上,从源开始,如下所示:

bool have_path(source, sink) {
    if source == sink {
        source.flag = true
        return true
    }

    // traverse all successor nodes of `source`
    for dst in succ(source) {
        if not dst.flag and not have_path(dst, sink)
           return false // exit as soon as we find a node with no path to `sink`
    }
    source.flag = true;
    return true
}
过程
have_path
在每个节点中设置一个布尔
标志
,该节点到接收器之间存在一些路径。同时,该过程只遍历可从源访问的节点,并遍历可从源访问的所有节点。如果该过程返回true,那么所有可从源访问的节点都位于到接收器的路径上。无法访问的节点已在第一阶段处理


[1] 将DFS编号较大的节点链接到DFS编号较小的节点的边,该边尚未完全处理,即仍在DFS堆栈上


[2] 例如,他们没有指定的DFS编号

查找强连接的组件。然后是源CC和接收器CC。检查它们是否都只包含一个元素。复杂性也是线性的。事实上,Tarjan的算法不是找到强连接组件的算法吗?你几乎是正确的,但仍然需要一个BFS或类似的算法来检查所有节点是否都能到达接收器,因为图中可以包含cycle:)@PhamTrung-这是真的。我假设这个图是已知的DAG,所有的假设都是表示邻接矩阵或列表。什么是没有的,您正在遍历时发现节点。那么你需要一种不同的方法吗?@alex-在这种情况下,你可以在图上做DFS或BFS来建立邻接列表或矩阵,然后可以使用这两种算法中的一种。