Algorithm 在有向图中使用DFS进行循环检测是否绝对需要回溯?

Algorithm 在有向图中使用DFS进行循环检测是否绝对需要回溯?,algorithm,depth-first-search,backtracking,directed-graph,cyclic,Algorithm,Depth First Search,Backtracking,Directed Graph,Cyclic,我遇到过这样的情况,有人建议在有向图中使用DFS进行循环检测会更快,因为有回溯。我在这里引用该链接: 深度优先搜索比广度优先搜索更节省内存,因为您可以更快地回溯。如果使用调用堆栈,则实现起来也更容易,但这取决于不溢出堆栈的最长路径 另外,如果你的图形是有方向的,那么你不仅要记住 如果您访问了一个节点与否,还需要了解您是如何到达该节点的。 否则,你可能会认为你已经找到了一个循环,但在现实中 你所拥有的只是两条独立的路径A->B,但这并不意味着 有一个路径B->a。通过深度优先搜索,您可以标记节点

我遇到过这样的情况,有人建议在有向图中使用DFS进行循环检测会更快,因为有回溯。我在这里引用该链接:

深度优先搜索比广度优先搜索更节省内存,因为您可以更快地回溯。如果使用调用堆栈,则实现起来也更容易,但这取决于不溢出堆栈的最长路径

另外,如果你的图形是有方向的,那么你不仅要记住 如果您访问了一个节点与否,还需要了解您是如何到达该节点的。 否则,你可能会认为你已经找到了一个循环,但在现实中 你所拥有的只是两条独立的路径A->B,但这并不意味着 有一个路径B->a。通过深度优先搜索,您可以标记节点 当你下山的时候,你会看到他们,当你回程的时候,他们会被取消标记

为什么回溯是必要的

有人能用示例图解释一下给定的
A->B
示例中的含义吗

最后,我有一个用于有向图中周期检测的
DFS
代码,它不使用回溯,但仍然在
O(E+V)
中检测周期

public boolean isCyclicDirected(顶点v){
如果(v.isvis){
返回true;
}
v、 setVisited(真);
迭代器e=v.adj.Iterator();
while(e.hasNext()){
顶点t=e.next().target;
//快速测试:
如果(t.isvis){
返回true;
}
//详细的递归测试:
如果(isCyclicDirected(t)){
返回true;
}
}
//没有一个相邻顶点标记为循环
v、 设定值(假);
返回false;
}

为什么需要回溯:

A -> B
^ \
|  v
D <- C
A -> B
  \  ^
   v |
     C
上面的图表没有循环,但是如果你去
A->B
,然后去
A->C->B
,如果你不记得路径,你会认为有循环


正如链接文章中所提到的,在返回代码之前,您可以将访问标志设置为false(我看到您现在已经这样做了)-这将起到记住路径的作用。

只有当您的图形没有任何情况下可以通过两条不同的路径从节点A到节点B时,回溯才是必不可少的。在前面的回答中提到的情况下,您的algo将检测到假阳性: A->B \ ^ 五| C
但是,如果添加回溯,即使在上述情况下,您的alto也会工作得很好。

值得指出的是,这种标记算法是对链表周期检测的原始方法的一种改进,它涉及到跟踪到目前为止访问的每个节点。在这种情况下,递归后的路径被视为链表,并应用链表算法。对于链表来说,空间复杂度是算法次优的原因,因为您需要保持对列表中每个节点的引用,它是O(n)。然而,当你把它应用到一个相当平衡的图上时,空间复杂度下降到O(logn)。在图是树的情况下,空间复杂度降低为O(n),但时间复杂度为O(n)


此外,算法仍然不正确。给定一个带有节点
a
B
以及单条边
B->B
的图,
isCyclicDirected(a)
将永远检测不到循环。

1。当我回溯时,我应该
设置visited
标志为false,在我进行下一次迭代之前,我引用上面
的内容,通过深度优先搜索,您可以在下降时将节点标记为已访问,并在回溯时取消标记。
,但我不会取消标记
已访问
。那你为什么说我回溯它不会停在那里(
A->B
)。看看我的代码,这是有效的,因为我迭代了
A
的所有相邻节点,也就是说,在我经过
A->B
之后,我回来做
A->C
和任何其他相邻节点。所以我认为回溯不是必要的,对不起,我现在只是把自己弄糊涂了。你做回溯是因为递归就是这样做的-你为一个邻居递归,如果没有找到循环,调用返回,你尝试其他邻居-这是回溯。是的,它不会在
A->B
处停止,但是根据我回答的第二部分,对于一些没有循环的图,您的代码将返回True。是的,您提供的示例图失败,但我修复了关闭
访问
标志的问题,现在它工作正常。因为我在做
setvisted(false)
,我在做回溯,我记得回溯模式是
set,recurse,unset
,在一个类似的注释中,我看到提供了一个带有回溯的dfs解决方案来查找图中的所有周期:第三个答案,但我不知道它是如何找到所有周期的,我在那里留下了注释。但是如果(t.isvisted){return true;}是不必要的,则没有得到回复
,因为调用isCyclicDirected(t)时,它要做的第一件事是检查传入的顶点是否已被访问。@Floegipoky:同意!很有道理!但是这个算法总体上是正确的,对吗?不,不幸的是它不正确。但是,如果您添加了
v.setVisited(false)
返回false之前我想是的!:)@弗洛伊吉波基:是的,我刚想出来。谢谢,我正在编辑帖子,你应该避免修改问题中的代码,因为这会使部分(部分或全部)答案无效。在这种情况下,它可能已经足够小了。请注意,如果只为一个顶点调用该函数,则该算法是不正确的,因为您提到的原因(如果为每个顶点调用该函数,则该算法将不起作用,但在每次调用后,所有已访问的标志也必须重置,这将使运行时间超过所提到的时间)。我认为,如果为每个顶点调用编辑的版本,它将返回正确的答案,但正如您所说的,运行时将不可接受。您应该能够通过在主调用中为访问的顶点创建另一个已访问数组来提高运行时间,您可以