为什么DFS而不是BFS在图中查找周期

时间:2010-05-19 21:42:54

标签: algorithm tree graph breadth-first-search depth-first-search

主要使用DFS来查找图形中的循环而不是BFS。有什么原因?两者都可以找到节点是否已经存在 遍历树/图时访问。

9 个答案:

答案 0 :(得分:59)

深度优先搜索比广度优先搜索更有效,因为您可以更快地回溯。如果使用调用堆栈,它也更容易实现,但这依赖于没有溢出堆栈的最长路径。

此外,如果你的图表是directed,那么你不仅要记住你是否访问了一个节点,还要记得你是如何到达那里的。否则你可能认为你已经找到了一个循环,但实际上你所拥有的是两个独立的路径A-> B但这并不意味着存在路径B-> A。 例如,

如果从0开始执行BFS,它将检测到周期存在但实际上没有周期。

使用深度优先搜索,您可以在下降时将节点标记为已访问,并在您回溯时将其取消标记。有关此算法的性能改进,请参阅注释。

对于best algorithm for detecting cycles in a directed graph,您可以查看Tarjan's algorithm

答案 1 :(得分:22)

  1. DFS更容易实施
  2. 一旦DFS找到一个循环,堆栈将包含形成循环的节点。对于BFS也是如此,因此如果您还要打印找到的循环,则需要做额外的工作。这使得DFS更方便。

答案 2 :(得分:9)

如果图表是无向的,那么BFS可能是合理的(我的客人在使用BFS显示有效算法时会报告有向图中的周期!),其中每个“交叉边缘”定义一个周期。如果交叉边缘为{v1, v2},并且包含这些节点的根(在BFS树中)为r,则周期为r ~ v1 - v2 ~ r~为路径, -单个边缘),几乎可以像在DFS中一样容易地报告。

使用BFS的唯一原因是,如果您知道您的(无向)图形将具有长路径和小路径覆盖(换句话说,深度和窄度)。在这种情况下,BFS比DFS'堆栈要求其队列的内存比例要小(当然两者都是线性的)。

在所有其他情况下,DFS显然是赢家。它适用于有向图和无向图,并且报告周期很简单 - 只需将任何后边缘连接到祖先的路径对后代,你得到了循环。总而言之,这个问题比BFS更好,更实用。

答案 3 :(得分:2)

如果你在一个树中的一个随机点放置一个循环,当它覆盖大约一半树时,DFS将倾向于达到循环,并且它将已经遍历循环的一半时间,并且一半时间它不会(并且会在树的其余部分平均找到它),因此它将平均评估约0.5 * 0.5 + 0.5 * 0.75 = 0.625的树。

如果在树中的随机点放置一个循环,BFS只有在评估该深度的树层时才会出现循环。因此,您通常最终必须评估平衡二叉树的叶子,这通常会导致评估更多的树。特别是,3/4的时间,两个链接中的至少一个出现在树的叶子中,在这些情况下,你必须平均评估树的3/4(如果有一个链接)或7 /树的8个(如果有两个),所以你已经达到预期的搜索1/2 * 3/4 + 1/4 * 7/8 =(7 + 12)/ 32 = 21/32 =树的0.656 ...甚至没有添加搜索树的成本,其中一个循环被添加到叶节点之外。

此外,DFS比BFS更容易实现。所以它是一个使用的,除非你知道你的周期(例如,周期可能在你搜索的根附近,此时BFS给你一个优势)。

答案 4 :(得分:2)

BFS在查找周期时无法使用有向图。考虑A-> B和A-> C-> B作为图中从A到B的路径。 BFS将在沿着B访问的路径之后说出来。当继续行进下一条路径时,它将再次找到标记的节点B,因此,存在一个循环。显然,这里没有循环。

答案 5 :(得分:1)

要证明图形是循环的,您只需要证明它有一个循环(边缘直接或间接指向自身)。

在DFS中,我们一次取一个顶点并检查它是否有循环。一旦找到一个循环,我们就可以省略检查其他顶点。

在BFS中,我们需要同时跟踪许多顶点边缘,并且最终会发现它是否具有周期。随着图形的大小增加,与DFS相比,BFS需要更多的空间,计算和时间。

答案 6 :(得分:0)

这取决于你是在谈论递归还是迭代实现。

递归-DFS两次访问每个节点。 Iterative-BFS访问每个节点一次。

如果要检测周期,则需要在添加其邻接之前和之后调查节点 - 无论是在节点上“开始”还是在“完成”节点时都是如此。

这需要在Iterative-BFS中进行更多工作,因此大多数人选择Recursive-DFS。

请注意,使用std :: stack的Iterative-DFS的简单实现与Iterative-BFS具有相同的问题。在这种情况下,您需要将虚拟元素放入堆栈中,以便在“完成”节点工作时进行跟踪。

有关Iterative-DFS如何确定何时“完成”节点(在TopoSort上下文中回答)的更多详细信息,请参阅此答案:

Topological sort using DFS without recursion

希望这能解释为什么人们喜欢Recursive-DFS来解决需要确定何时“完成”处理节点的问题。

答案 7 :(得分:0)

我不知道为什么我的供稿中会弹出这样一个老问题,但是以前的所有答案都是不好的,所以...

DFS用于查找有向图中的循环,因为它可以正常工作。

在DFS中,每个顶点都“被访问”,访问顶点意味着:

  1. 顶点已开始
  2. 从该顶点可访问的子图被访问。这包括跟踪从该顶点可到达的所有未跟踪的边,并访问所有可到达的未访问的顶点。

  3. 顶点完成了。

关键特征是在顶点完成之前要跟踪从顶点可到达的所有边。这是DFS的功能,但不是BFS的功能。实际上,这就是DFS的定义。

由于此功能,我们知道在周期中的 first 顶点开始时:

  1. 循环中没有任何一条边被追踪。我们知道这一点,因为您只能在循环中从另一个顶点到达它们,而我们正在谈论要开始的 first 顶点。
  2. 该顶点可到达的所有未跟踪边缘将在完成之前被跟踪,并且包括循环中的所有边缘,因为尚未跟踪任何边缘。因此,如果有一个循环,我们将在开始后但在完成之前找到回到第一个顶点的边;和
  3. 由于从每个开始但未完成的顶点都可以跟踪到所有的边缘,因此找到这样一个顶点的边缘总是表示一个循环。

因此,如果有一个循环,那么我们可以保证找到一个起始但未完成的顶点(2)的边,如果找到这样的边,那么我们可以保证存在一个循环(3 )。

这就是为什么使用DFS在有向图中查找循环的原因。

BFS不提供此类保证,因此它行不通。 (尽管包含BFS或类似的子过程的循环查找算法非常好)

另一方面,当任何一对顶点之间有两条路径时,即当它不是一棵树时,无向图都有一个循环。这在BFS或DFS期间都很容易检测到-跟踪到新顶点的边形成一棵树,其他任何边都表示一个周期。

答案 8 :(得分:0)

要在有向图中查找包含给定节点的最短循环时,必须使用BFS

例如:enter image description here

如果给定节点为2,则在三个周期中,它是-[2,3,4][2,3,4,5,6,7,8,9][2,5,6,7,8,9]的一部分。最短的是[2,3,4]

要使用BFS实施此操作,必须使用适当的数据结构显式维护访问的节点的历史记录。

但是出于所有其他目的(例如:查找任何循环路径或检查是否存在循环),出于其他人提到的原因,DFS是明确的选择。

相关问题