Graph 如何检测有向图是否是循环的?

Graph 如何检测有向图是否是循环的?,graph,breadth-first-search,cyclic,Graph,Breadth First Search,Cyclic,如何检测有向图是否为循环图?我想使用广度优先搜索,但我不确定。有什么想法吗 通常使用深度优先搜索。我不知道BFS是否容易应用 在中,按访问顺序构建生成树。如果访问了树中某个节点的祖先(即创建了后缘),则我们会检测到一个循环 请参阅以获得更详细的解释。另一个简单的解决方案是标记和扫描方法。基本上,对于树中的每个节点,您将其标记为“已访问”,然后移动到其子节点。如果您看到设置了“visted”标志的节点,您就知道存在一个循环 如果无法修改图形以包含“已访问”位,则可以使用一组节点指针。要将节点标记为

如何检测有向图是否为循环图?我想使用广度优先搜索,但我不确定。有什么想法吗

通常使用深度优先搜索。我不知道BFS是否容易应用

在中,按访问顺序构建生成树。如果访问了树中某个节点的祖先(即创建了后缘),则我们会检测到一个循环


请参阅以获得更详细的解释。

另一个简单的解决方案是标记和扫描方法。基本上,对于树中的每个节点,您将其标记为“已访问”,然后移动到其子节点。如果您看到设置了“visted”标志的节点,您就知道存在一个循环


如果无法修改图形以包含“已访问”位,则可以使用一组节点指针。要将节点标记为已访问,请在集合中放置指向该节点的指针。如果指针已经在集合中,则存在一个循环。

我认为,您真正需要的是一个拓扑排序算法,如下面所述:

如果有向图有一个循环,那么算法将失败

到目前为止,我看到的评论/回复似乎忽略了一个事实,即在有向图中,从节点X到节点Y可能有多种方式,而图中没有任何(有向)循环。

方法:1
一个级别不分配来检测循环如何。考虑下面的图表。A->(B,C)B->D->(E,F)E,F->(G)E->D执行DFS时,开始为您访问的节点分配级别编号(根A=0)。节点的级别号=父节点+1。所以A=0,B=1,D=2,F=3,G=4,然后递归到达D,所以E=3。不要为G标记级别(G已经分配了一个比E更高的级别编号),现在E也比D有优势。所以水平化会说D应该得到一个级别编号4。但是D已经有了一个2的“较低级别”。因此,当您在执行已设置较低级别编号的DFS时,尝试为节点分配级别编号时,您知道有向图有一个循环

方法2:
使用3种颜色。白色,灰色,黑色。只对白色节点进行着色,沿着DFS向下移动时将白色节点变为灰色,递归展开时将灰色节点变为黑色(所有子节点都已处理)。如果不是所有的子节点都被处理,你会碰到一个灰色的节点,这就是一个循环。 例:全白开始在上面的直接图中。
颜色A、B、D、F、G为白灰色。G是叶子,所以所有的孩子都把它涂成灰色到黑色。递归将展开为F(所有已处理的子对象)并将其着色为黑色。现在你到达D,D有未处理的子对象,所以E颜色为灰色,G颜色为黑色,所以不要再往下走。E也有边到D,因此在仍然处理D(D仍然是灰色的)时,您会发现边回到D(灰色节点),检测到一个循环。

测试给定图形的拓扑排序将引导您找到解决方案。如果topsort算法(即边应始终以一种方式定向)失败,则表示图形包含循环。

使用DFS搜索任何循环路径

class Node<T> { T value; List<Node<T>> adjacent;  }

class Graph<T>{

    List<Node<T>> nodes;

   public boolean isCyclicRec()
   {

      for (Node<T> node : nodes)
      {
            Set<Node<T>> initPath = new HashSet<>();
            if (isCyclicRec(node, initPath))
            {
              return true;
            }
      }
      return false;
   }

   private boolean isCyclicRec(Node<T> currNode, Set<Node<T>> path)
   {
      if (path.contains(currNode))
      {
        return true;
      }
      else
      {
        path.add(currNode);
        for (Node<T> node : currNode.adjacent)
        {
            if (isCyclicRec(node, path))
            {
                return true;
            }
            else
            {
                path.remove(node);
            }
        }
      }
      return false;
  }
类节点{T值;列表相邻;}
类图{
列出节点;
公共布尔值isCyclicRec()
{
用于(节点:节点)
{
Set initPath=new HashSet();
if(isCyclicRec(节点,initPath))
{
返回true;
}
}
返回false;
}
专用布尔值isCyclicRec(节点currNode,设置路径)
{
if(path.contains(currNode))
{
返回true;
}
其他的
{
添加路径(currNode);
用于(节点:currNode.nextant)
{
if(isCyclicRec(节点,路径))
{
返回true;
}
其他的
{
移除(节点);
}
}
}
返回false;
}

BFS或DFS在此问题上具有相同的复杂性(运行时)和适用性(相同的结果)。唯一的区别在于节点的访问顺序。链接中显示的页面上的最后一条语句是基于边和顶点数量的拓扑语句:“如果图G没有圈,则图G中可能的最大边数为| V |-1。”这适用于无向图,但不适用于有向图,如原问题所示。对于有向图,“菱形继承”图提供了一个反例(4个节点和4条边构成“循环”,但不是“循环”)如果最后一条评论不清楚的话,我是说接受的答案对于无向图是足够的,但是对于有向图是错误的(如问题中所述)@Peter:前面的链接可能是错误的,但我所描述的概念即使对于有向图也是正确的。我使术语更清晰,并提供了更好的来源。@darlinton:但是做
BFS
是一种过分的做法,因为同样的事情也可以通过更简单的
DFS
来实现。这个解决方案类似于KennyTM提供的解决方案,但是解决这个问题更好。问题是识别循环,所以标记和扫描是一个好方法。正如KennyTM所建议的,构建生成树是解决这个问题更复杂的方法,因为你使用的是生成树的概念。最后,几乎是一样的。@Rakis:它会错误地识别为循环吗?@KennyTM:是的,我不会。如果你使用DFS或BFS来探索图中的节点,这并不重要——无论哪种方式,你都会得到相同的错误答案。仅仅记录访问过哪些节点来识别周期是不够的。因此,我会对Rakis的答案扣一分(但我的声誉太低,无法做到这一点).啊,确实是这样。我想我想的是一棵树,而不是一个实际的图。但是,我相信同样的一般方法在这种情况下仍然有效,跟踪访问的边而不是访问的节点。使用一组(从节点id到节点id)对将检测到多次遍历同一条边