确定有向图是否是单边的

时间:2013-12-20 01:18:11

标签: algorithm graph directed-graph adjacency-list

你如何找出有向图是否是单边的(对于任何一对顶点uv,至少有一个可以从另一对中到达)? 我想你可以运行DFS或BFS,看看你是否可以到达每个顶点。如果不是,请计算转置并从同一顶点执行相同的搜索算法。如果你已经到达每个顶点至少一个,那么图是单边的吗?

显然,你可以通过分析邻接矩阵在大型运行时间内完成这项工作,但理想情况下我们希望在O(V + E)中运行

3 个答案:

答案 0 :(得分:0)

试试these algorithms。我在大学里学到的那个是Floyd-Warshall,但它的表现并不像你想要的那么好。对于稀疏图,Johnsons's algorithm更快,但仍然是O(V * E),而不是O(V + E)。

答案 1 :(得分:0)

我不完全确定这是否正确,但我认为检查有向图是否“连接”应该足够了(请参阅下面的算法说明,找出我所说的连接方式)。如果有向图中有多个连通分量,那么它不是单边的。

证明尝试:

假设有向图有多个连通分量。为简单起见,让连接组件的数量为2,让我们调用组件C1C2。从v中选择任意顶点C1,从w选择任何顶点C2。由于它们位于不同的连接组件中,因此从vwwv没有路径,否则C1C2将会在同一个组成部分。

对于另一种情况,假设连接了有向图。然后,对于有向图中的任意2个不同顶点xy,存在从xy或从y到{{1}的路径},否则它们不会在同一个组件中。我知道这里有一点波浪状,但我并不擅长证明。

编辑:更简单的算法:

我确实认为经过修改的深度优先搜索 / 广度优先搜索应该能够做到。基本上,我们可以从任何顶点开始搜索。我们将所有可从第一个顶点到达的顶点标记为已访问。然后循环遍历所有顶点。对于任何未访问的顶点,我们进行BFS / DFS,并使用列表来跟踪我们遍历的所有未访问的顶点。如果在BFS / DFS期间,我们不访问先前BFS / DFS中任何先前访问过的顶点,那么我们可以立即断定有向图具有多个连通分量且不是单边的。否则,我们将搜索过程中获得的列表中的所有顶点标记为已访问,然后继续。

一旦我们遍历了图中的所有顶点,所有BFS / DFS都会击中一些先前访问过的顶点,我们可以得出结论图是单边的。

一些伪代码来说明这一点。我将使用深度优先搜索:

x

原始的,更难处理的算法:

我们如何计算有向图是否是单边的?您可以先运行Tarjan's Strongly Connected Components algorithm并确定有向图的强连接组件。您需要稍微修改算法以将强连接组件压缩到超级转换中。这并不难,只需将同一个强连接组件中的每个顶点分配一个整数标签即可。换句话说,同一个强连通分量中的所有顶点将被1个超变量替换。 Tarjan的SCC(强连接组件)算法在// a boolean array to keep track if a given vertex is visited // initially this is set to false boolean[] visited // boolean array to keep track of visited vertices for 2nd dfs onwards boolean[] visited_two // used for first dfs dfs(vertex) { visited[vertex] <- true for each edge leading out of vertex { dfs(next vertex) } } // used for subsequent dfs dfs_two(vertex, list_for_storing_vertices) { // we use the visited_two array here, because the // visited array is used to store previously visited // vertices visited_two[vertex] <- true list_for_storing_vertices.append(vertex) // return value. we return true if we encounter // a previously visited vertex. otherwise, return false return_value <- false for each edge leading out of vertex { if visited[next vertex] return_value <- true else if !visited_two[next_vertex] r = dfs_two(next vertex, list_for_storing_vertices) return_value = return_value || r } return return_value } // overall algorithm // Returns true if the graph is unilateral. false otherwise bool digraph_unilateral(graph) { // Just pick any vertex. we will pick vertex 0 set all entries in `visited` array to false dfs(0) // then loop through all vertices. if they haven't been visited, // we perform a dfs for each vertex in the digraph { if !visited[vertex] { // reset visited_two array set all entries in `visited_two` array to false visited_list <- [] encountered_previously_visited <- dfs_two(vertex, visited_list) if encountered_previously_visited { // mark the vertices we encountered as visited for each v in visited_list { visited[v] <- true } } else { // we did not encounter any previously visited vertex // so all vertices we encounter on this search are in // a distinct connected component return false } } } return true } 时间内运行。

在上面的步骤之后,有向图现在是非循环(无循环)。这使我们可以继续下一步。

之后,从上面对结果图执行拓扑排序。这应该给我们一个有向无环图的拓扑排序。此步骤也使用深度优先搜索实现在O(V+E)时间运行。

现在我们有拓扑排序,根据上的拓扑排序执行广度优先搜索深度优先搜索 > Tarjan的SCC步骤后获得的>有向无环图。如果连通分量的数量是1,那么原始图是单边的(如果连通分量的数量是0,它也是单边的,但这是一个空图,因此是微不足道的)。否则,有多个组件,原始图不是单边的。此步骤也在O(V+E)时间内运行。

因此,整体算法在O(V+E)时间内运行。

<强>结论

我认为这应该是正确的,但您可能希望与其他人验证此方法。如果您在理解我的方法或实施算法时遇到任何困难,请发表评论。

希望有所帮助!

答案 2 :(得分:0)

使用Tarjan's algorithm查找牢固连接的组件。 SCC中的每个节点都可以相互访问,因此就可以到达和到达的节点而言,它们是等效的。将每个SCC折叠为一个顶点,如果原始图是单边的,则生成的DAG将是单边的。

如果DAG是整体排序,即只有一个拓扑顺序,则DAG是单方面的。如果存在从A到B的路径,那么A必须在B之前。如果存在从B到A的路径,那么B必须在A之前。您不能同时拥有两者,因为该图现在是非循环的。如果A和B之间没有路径,则它们没有顺序,并且该图至少有2个拓扑顺序-一个在B之前具有A,在A之前具有B。

检查总订单数的快速方法是使用Kahn算法进行topological sort,并检查以确保每次迭代的下一个顶点只有一个选择。

Tarjan用于查找SCC,折叠SCC的算法以及Kahn用于拓扑排序的算法都在O(V + E)时间内运行。