我看到了这篇SO帖子,其中建议在有向图中使用DFS进行循环检测由于回溯而更快。这里我引用该链接:
深度优先搜索比广度优先搜索更节省内存,因为您可以更快地回溯。如果使用调用堆栈,则实现起来也更容易,但这取决于不溢出堆栈的最长路径。
如果你的图是有方向的,那么你不仅要记住你是否访问过一个节点,还要记住你是如何到达的。否则,你可能会认为你已经找到了一个循环,但在现实中,你所拥有的只是两条不同的路径-
为什么回溯是必要的?
有人能用示例图解释一下给定A中的含义吗-
最后,我有一个用于有向图中循环检测的
DFS
代码,它不使用回溯,但仍然在O(E V)
中检测循环。
public boolean isCyclicDirected(Vertex v){
if (v.isVisited) {
return true;
}
v.setVisited(true);
Iterator<Edge> e = v.adj.iterator();
while (e.hasNext()) {
Vertex t = e.next().target;
// quick test:
if (t.isVisited) {
return true;
}
// elaborate, recursive test:
if (isCyclicDirected(t)) {
return true;
}
}
// none of our adjacent vertices flag as cyclic
v.setVisited(false);
return false;
}
只有当您的图没有任何可以通过两条不同路径从节点A到节点B的情况下,回溯才是必要的。你的算法将检测到一个假阳性的情况下,提到在前面的答案:A-
值得指出的是,这种标记算法是对链表周期检测的朴素方法的一种改进,这种方法涉及到跟踪到目前为止访问的每个节点。在这种情况下,递归后的路径被视为链表,并应用链表算法。对于链表来说,空间复杂度是算法次优的原因,因为您需要保持对列表中每个节点的引用,它是O(n)。然而,当你把它应用到一个相当平衡的图上时,空间复杂度下降到O(logn)。在图是树的情况下,空间复杂度降低为O(n),但时间复杂度为O(n)。
此外,算法仍然不正确。给定一个具有节点a
和B
以及单条边B的图-
您需要回溯的原因:
A -> B
^ \
| v
D <- C
如果你去A-
你的算法会回溯。您只是用递归包装它,所以它可能看起来不太像您期望的那样。你递归为一个邻居,如果这没有找到一个循环,那个调用返回,你尝试其他邻居——这是回溯。
为什么你需要记住你是如何到达现在的位置的:
A -> B
\ ^
v |
C
上图没有循环,但是如果你去
A-
正如链接文章中提到的,在返回代码之前,您可以将访问标志设置为false(我看到您现在已经这样做了)——这将起到记住路径的作用。
另一个问题只回答了如何检测一个周期,而不是输出它。所以,我想在一个无向图上写一个算法,在O(V+E)时间内运行BFS或DFS(V=顶点,E=边),如果有循环,则输出循环。 到目前为止,我所知道的是BFS/DFS是如何工作的,如果访问已经标记为已访问的节点,可以使用BFS检测周期。
因此,我为DFS编写了以下代码: 现在,我读到,在一个无向图中,如果当DFS时,它再次返回到同一个顶点,有一个循环。所以我做的是这样,, 但是,我的检查周期函数返回true,即使他们没有周期。那是为什么呢?我的功能有问题吗?没有执行问题,否则我早就调试了,但他们似乎在我的逻辑中有问题。
我在这里读到一篇关于在有向图中求圈的讨论。现在,OP声称我们需要验证两件事: 从到有一个后沿 在递归堆栈中 为什么我们需要第二次测试?你能举个例子来说明它的必要性吗?
目前我正在用这个样本进行拓扑排序,并对https://www.geeksforgeeks.org/topological-sorting/做了一些修改 我用它来排序要求解的变量的顺序。 样本 每个变量都有一个唯一整数,并存储在一个映射中 创建图形并添加边时,总共有4个顶点,因此我的代码将像这样构造图形 排序并按相反顺序得到结果后,它是正确的c 一切都很好,但我需要检测图中的循环引用。假设变量是 有
我试图用BFS算法在有向图中检测循环。我检测周期的主要想法是:由于BFS只访问每个节点(和边缘)一次,如果我再次遇到已访问的节点;它会导致一个循环。然而,我的代码有时会找到循环,有时不会。 我从维基百科修改的伪代码如下: 我错过了什么?
我有一个带边的无向图。每条边都具有一定的性质,如A点和B点之间的边之一是 在C点和D点之间的另一个可以是 我们得到了这样一个图和已知的旅行路径 有快一点的吗?