你如何找出有向图是否是单边的(对于任何一对顶点u
,v
,至少有一个可以从另一对中到达)?
我想你可以运行DFS或BFS,看看你是否可以到达每个顶点。如果不是,请计算转置并从同一顶点执行相同的搜索算法。如果你已经到达每个顶点至少一个,那么图是单边的吗?
显然,你可以通过分析邻接矩阵在大型运行时间内完成这项工作,但理想情况下我们希望在O(V + E)中运行
答案 0 :(得分:0)
试试these algorithms。我在大学里学到的那个是Floyd-Warshall,但它的表现并不像你想要的那么好。对于稀疏图,Johnsons's algorithm更快,但仍然是O(V * E),而不是O(V + E)。
答案 1 :(得分:0)
我不完全确定这是否正确,但我认为检查有向图是否“连接”应该足够了(请参阅下面的算法说明,找出我所说的连接方式)。如果有向图中有多个连通分量,那么它不是单边的。
证明尝试:
假设有向图有多个连通分量。为简单起见,让连接组件的数量为2,让我们调用组件C1
和C2
。从v
中选择任意顶点C1
,从w
选择任何顶点C2
。由于它们位于不同的连接组件中,因此从v
到w
或w
到v
没有路径,否则C1
和C2
将会在同一个组成部分。
对于另一种情况,假设连接了有向图。然后,对于有向图中的任意2个不同顶点x
和y
,存在从x
到y
或从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)时间内运行。