【问题标题】:Finding all cycles in a directed graph在有向图中查找所有循环
【发布时间】:2010-10-07 11:52:28
【问题描述】:

如何在有向图中从/到给定节点找到(迭代)所有循环?

例如,我想要这样的东西:

A->B->A
A->B->C->A

但不是: B->C->B

【问题讨论】:

标签: algorithm graph-theory graph-algorithm


【解决方案1】:

从节点 X 开始并检查所有子节点(如果无向,则父节点和子节点是等效的)。将这些子节点标记为 X 的子节点。从任何这样的子节点 A 中,将其标记为 A 的子节点 X',其中 X' 被标记为距离 2 步。)。如果您稍后点击 X 并将其标记为 X'' 的子级,则表示 X 处于 3 节点循环中。回溯到它的父级很容易(因为算法不支持这一点,所以你会找到任何一个有 X' 的父级)。

注意:如果图是无向的或有任何双向边,则此算法会变得更加复杂,假设您不想在一个循环中遍历同一条边两次。

【讨论】:

    【解决方案2】:

    我曾经收到过这个面试问题,我怀疑这件事发生在你身上,你来这里寻求帮助。把问题分解成三个问题,就容易多了。

    1. 你如何确定下一个有效的 路线
    2. 如何确定一个点是否有 被使用了
    3. 如何避免穿越 又是同一点

    问题 1) 使用迭代器模式提供一种迭代路由结果的方法。放置逻辑以获取下一条路线的好地方可能是迭代器的“moveNext”。要找到有效的路线,这取决于您的数据结构。对我来说,这是一个充满有效路线可能性的 sql 表,因此我必须构建一个查询来获取给定源的有效目的地。

    问题 2) 当你找到它们时将每个节点推入一个集合中,这意味着你可以通过询问你正在构建的集合来很容易地查看你是否在一个点上“加倍”。

    问题 3) 如果在任何时候你看到你正在加倍回来,你可以从收藏中弹出东西并“备份”。然后从那时起再次尝试“前进”。

    Hack:如果您使用的是 Sql Server 2008,如果您在树中构建数据,则可以使用一些新的“层次结构”来快速解决这个问题。

    【讨论】:

      【解决方案3】:

      带有回溯的深度优先搜索应该在这里工作。 保留一个布尔值数组以跟踪您之前是否访问过某个节点。如果你用完了新节点可以去(没有碰到你已经去过的节点),那么只需回溯并尝试不同的分支。

      如果你有一个邻接表来表示图,那么 DFS 很容易实现。例如 adj[A] = {B,C} 表示 B 和 C 是 A 的孩子。

      例如下面的伪代码。 “start”是你开始的节点。

      dfs(adj,node,visited):  
        if (visited[node]):  
          if (node == start):  
            "found a path"  
          return;  
        visited[node]=YES;  
        for child in adj[node]:  
          dfs(adj,child,visited)
        visited[node]=NO;
      

      以起始节点调用上述函数:

      visited = {}
      dfs(adj,start,visited)
      

      【讨论】:

      • 谢谢。与这里提到的其他一些方法相比,我更喜欢这种方法,因为它易于理解并且具有合理的时间复杂度,尽管可能不是最佳的。
      • 这是如何找到所有循环的?
      • if (node == start): -- 第一次通话中的node and start 是什么
      • @user1988876 这似乎找到了涉及给定顶点的所有循环(即start)。它从那个顶点开始执行 DFS,直到它再次回到那个顶点,然后它知道它找到了一个循环。但它实际上并没有输出周期,只是它们的计数(但修改它来做到这一点应该不会太难)。
      • @user1988876 好吧,它只是打印“找到路径”的次数等于找到的周期数(这可以很容易地用计数代替)。是的,它只会检测来自start 的周期。由于visited[node]=NO;,您实际上并不需要清除已访问标志,因为每个已访问标志都将被清除。但请记住,如果您有一个循环 A->B->C->A,您将检测到 3 次,因为 start 可以是其中的任意 3 次。防止这种情况的一个想法是设置另一个已访问的数组,其中设置了在某个时间点已成为 start 节点的每个节点,然后您无需重新访问这些。
      【解决方案4】:

      【讨论】:

      • 问题是关于去除有向图中的循环,但本文档是关于无向图的。
      【解决方案5】:

      我在搜索中找到了这个页面,由于循环与强连接组件不同,我继续搜索,最后,我找到了一个有效的算法,它列出了有向图的所有(基本)循环。它来自 Donald B. Johnson,该论文可在以下链接中找到:

      http://www.cs.tufts.edu/comp/150GA/homeworks/hw1/Johnson%2075.PDF

      可以在以下位置找到 java 实现:

      http://normalisiert.de/code/java/elementaryCycles.zip

      约翰逊算法的Mathematica演示可以在here找到,实现可以从右边下载("Download author code")。

      注意:其实这个问题有很多算法。本文中列出了其中一些:

      http://dx.doi.org/10.1137/0205007

      根据文章,约翰逊的算法是最快的。

      【讨论】:

      • 我发现从论文中实现起来很麻烦,最终这个算法仍然需要一个 Tarjan 的实现。 Java代码也很可怕。 :(
      • @Gleno 好吧,如果您的意思是可以使用 Tarjan 查找图中的所有循环而不是实现其余循环,那您就错了。 Here,你可以看到强连接组件和所有循环的区别(Tarjan的alg不会返回循环cd和gh)(@batbrat你困惑的答案也隐藏在这里:所有可能的循环都不会返回由 Tarjan 的算法,所以它的复杂性可能小于指数)。 Java 代码可能会更好,但它节省了我从论文中实现的工作量。
      • 这个答案比选择的答案好很多。我挣扎了很长时间,试图弄清楚如何从强连接的组件中获得所有简单的循环。事实证明这是不平凡的。 Johnson 的论文包含一个很棒的算法,但有点难以通过。我查看了 Java 实现并在 Matlab 中推出了自己的实现。代码可在gist.github.com/1260153 获得。
      • @moteutsch:也许我遗漏了一些东西,但根据约翰逊论文(和其他来源),如果没有顶点(除了开始/结束)出现不止一次,则循环是基本的。按照这个定义,A->B->C->A 不也是基本的吗?
      • 使用 python 的人请注意:Johnson 算法在 networkx 中实现为 simple_cycle
      【解决方案6】:

      我偶然发现了以下算法,它似乎比约翰逊的算法更有效(至少对于较大的图表)。但是,与 Tarjan 的算法相比,我不确定它的性能。
      此外,到目前为止,我只检查了三角形。如果有兴趣,请参阅 Norishige Chiba 和 Takao Nishizeki 的《Arboricity and Subgraph Listing Algorithms》(http://dx.doi.org/10.1137/0214017

      【讨论】:

        【解决方案7】:

        如果您想要在图中找到所有基本电路,您可以使用 JAMES C. TIERNAN 的 EC 算法,该算法自 1970 年以来在一篇论文中发现。

        非常原始的 EC 算法,我设法在 php 中实现它(希望没有错误如下所示)。如果有的话,它也可以找到循环。此实现中的电路(尝试克隆原始电路)是非零元素。这里的零代表不存在(我们知道它是 null)。

        除了下面的实现之外,还有一个使算法更加独立的实现,这意味着节点可以从任何地方开始,甚至可以从负数开始,例如 -4、-3、-2、.. 等。

        在这两种情况下,都要求节点是连续的。

        您可能需要研究原始论文,James C. Tiernan Elementary Circuit Algorithm

        <?php
        echo  "<pre><br><br>";
        
        $G = array(
                1=>array(1,2,3),
                2=>array(1,2,3),
                3=>array(1,2,3)
        );
        
        
        define('N',key(array_slice($G, -1, 1, true)));
        $P = array(1=>0,2=>0,3=>0,4=>0,5=>0);
        $H = array(1=>$P, 2=>$P, 3=>$P, 4=>$P, 5=>$P );
        $k = 1;
        $P[$k] = key($G);
        $Circ = array();
        
        
        #[Path Extension]
        EC2_Path_Extension:
        foreach($G[$P[$k]] as $j => $child ){
            if( $child>$P[1] and in_array($child, $P)===false and in_array($child, $H[$P[$k]])===false ){
            $k++;
            $P[$k] = $child;
            goto EC2_Path_Extension;
        }   }
        
        #[EC3 Circuit Confirmation]
        if( in_array($P[1], $G[$P[$k]])===true ){//if PATH[1] is not child of PATH[current] then don't have a cycle
            $Circ[] = $P;
        }
        
        #[EC4 Vertex Closure]
        if($k===1){
            goto EC5_Advance_Initial_Vertex;
        }
        //afou den ksana theoreitai einai asfales na svisoume
        for( $m=1; $m<=N; $m++){//H[P[k], m] <- O, m = 1, 2, . . . , N
            if( $H[$P[$k-1]][$m]===0 ){
                $H[$P[$k-1]][$m]=$P[$k];
                break(1);
            }
        }
        for( $m=1; $m<=N; $m++ ){//H[P[k], m] <- O, m = 1, 2, . . . , N
            $H[$P[$k]][$m]=0;
        }
        $P[$k]=0;
        $k--;
        goto EC2_Path_Extension;
        
        #[EC5 Advance Initial Vertex]
        EC5_Advance_Initial_Vertex:
        if($P[1] === N){
            goto EC6_Terminate;
        }
        $P[1]++;
        $k=1;
        $H=array(
                1=>array(1=>0,2=>0,3=>0,4=>0,5=>0),
                2=>array(1=>0,2=>0,3=>0,4=>0,5=>0),
                3=>array(1=>0,2=>0,3=>0,4=>0,5=>0),
                4=>array(1=>0,2=>0,3=>0,4=>0,5=>0),
                5=>array(1=>0,2=>0,3=>0,4=>0,5=>0)
        );
        goto EC2_Path_Extension;
        
        #[EC5 Advance Initial Vertex]
        EC6_Terminate:
        print_r($Circ);
        ?>
        

        然后这是另一个实现,更独立于图形,没有 goto 和没有数组值,而是使用数组键,路径,图形和电路存储为数组键(如果你愿意,可以使用数组值,只是更改所需的行)。示例图从 -4 开始以显示其独立性。

        <?php
        
        $G = array(
                -4=>array(-4=>true,-3=>true,-2=>true),
                -3=>array(-4=>true,-3=>true,-2=>true),
                -2=>array(-4=>true,-3=>true,-2=>true)
        );
        
        
        $C = array();
        
        
        EC($G,$C);
        echo "<pre>";
        print_r($C);
        function EC($G, &$C){
        
            $CNST_not_closed =  false;                          // this flag indicates no closure
            $CNST_closed        = true;                         // this flag indicates closure
            // define the state where there is no closures for some node
            $tmp_first_node  =  key($G);                        // first node = first key
            $tmp_last_node  =   $tmp_first_node-1+count($G);    // last node  = last  key
            $CNST_closure_reset = array();
            for($k=$tmp_first_node; $k<=$tmp_last_node; $k++){
                $CNST_closure_reset[$k] = $CNST_not_closed;
            }
            // define the state where there is no closure for all nodes
            for($k=$tmp_first_node; $k<=$tmp_last_node; $k++){
                $H[$k] = $CNST_closure_reset;   // Key in the closure arrays represent nodes
            }
            unset($tmp_first_node);
            unset($tmp_last_node);
        
        
            # Start algorithm
            foreach($G as $init_node => $children){#[Jump to initial node set]
                #[Initial Node Set]
                $P = array();                   // declare at starup, remove the old $init_node from path on loop
                $P[$init_node]=true;            // the first key in P is always the new initial node
                $k=$init_node;                  // update the current node
                                                // On loop H[old_init_node] is not cleared cause is never checked again
                do{#Path 1,3,7,4 jump here to extend father 7
                    do{#Path from 1,3,8,5 became 2,4,8,5,6 jump here to extend child 6
                        $new_expansion = false;
                        foreach( $G[$k] as $child => $foo ){#Consider each child of 7 or 6
                            if( $child>$init_node and isset($P[$child])===false and $H[$k][$child]===$CNST_not_closed ){
                                $P[$child]=true;    // add this child to the path
                                $k = $child;        // update the current node
                                $new_expansion=true;// set the flag for expanding the child of k
                                break(1);           // we are done, one child at a time
                    }   }   }while(($new_expansion===true));// Do while a new child has been added to the path
        
                    # If the first node is child of the last we have a circuit
                    if( isset($G[$k][$init_node])===true ){
                        $C[] = $P;  // Leaving this out of closure will catch loops to
                    }
        
                    # Closure
                    if($k>$init_node){                  //if k>init_node then alwaya count(P)>1, so proceed to closure
                        $new_expansion=true;            // $new_expansion is never true, set true to expand father of k
                        unset($P[$k]);                  // remove k from path
                        end($P); $k_father = key($P);   // get father of k
                        $H[$k_father][$k]=$CNST_closed; // mark k as closed
                        $H[$k] = $CNST_closure_reset;   // reset k closure
                        $k = $k_father;                 // update k
                }   } while($new_expansion===true);//if we don't wnter the if block m has the old k$k_father_old = $k;
                // Advance Initial Vertex Context
            }//foreach initial
        
        
        }//function
        
        ?>
        

        我已经分析并记录了 EC,但不幸的是,文档是希腊语的。

        【讨论】:

          【解决方案8】:

          首先 - 你并不真的想尝试从字面上找到所有的循环,因为如果有 1,那么这些循环的数量是无限的。例如 ABA、ABABA 等。或者可以将 2 个循环组合成一个类似 8 的循环等,等等......有意义的方法是寻找所有所谓的简单循环 - 那些不交叉的循环,除了在起点/终点。然后,如果您愿意,您可以生成简单循环的组合。

          在有向图中查找所有简单循环的基线算法之一是:对图中的所有简单路径(不与自身交叉的路径)进行深度优先遍历。每次当前节点在堆栈上有一个后继节点时,都会发现一个简单的循环。它由堆栈上的元素组成,从确定的后继元素开始,到堆栈的顶部结束。所有简单路径的深度优先遍历类似于深度优先搜索,但除了当前在堆栈中的节点之外,您不会将访问过的节点标记/记录为停止点。

          上面的蛮力算法效率极低,此外还会生成多个循环副本。然而,它是多种实用算法的起点,这些算法应用各种增强功能以​​提高性能并避免循环重复。前段时间我惊讶地发现,这些算法在教科书和网络上并不容易获得。所以我做了一些研究,在一个开源 Java 库中实现了 4 个这样的算法和 1 个循环算法:http://code.google.com/p/niographs/

          顺便说一句,因为我提到了无向图:这些算法是不同的。构建一棵生成树,然后不属于树的每条边与树中的一些边一起形成一个简单的循环。以这种方式找到的循环形成所谓的循环基础。然后可以通过组合 2 个或更多不同的基本循环来找到所有简单循环。有关更多详细信息,请参见例如这个:http://dspace.mit.edu/bitstream/handle/1721.1/68106/FTL_R_1982_07.pdf.

          【讨论】:

          【解决方案9】:

          在 DAG 中查找所有循环涉及两个步骤(算法)。

          第一步是使用Tarjan算法找到强连通分量的集合。

          1. 从任意顶点开始。
          2. 来自该顶点的 DFS。对于每个节点 x,保留两个数字,dfs_index[x] 和 dfs_lowval[x]。 dfs_index[x] 存储访问该节点的时间,而 dfs_lowval[x] = min(dfs_low[k]) 其中 k 是 x 在 dfs 生成树中不是 x 的直接父级的所有子级。
          3. 具有相同 dfs_lowval[x] 的所有节点都在同一个强连通分量中。

          第二步是在连接的组件中找到循环(路径)。我的建议是使用 Hierholzer 算法的修改版本。

          想法是:

          1. 选择任何起始顶点 v,然后沿着从该顶点开始的边轨迹,直到返回 v。 不可能卡在除 v 之外的任何顶点,因为所有顶点的偶数度确保当路径进入另一个顶点 w 时,必须有一条未使用的边离开 w。这样形成的游览是一个封闭的游览,但可能不会覆盖初始图的所有顶点和边。
          2. 只要存在属于当前游览但相邻边不属于游览的顶点 v,则从 v 开始另一条路径,沿着未使用的边直到返回 v,并加入在此形成的游览上一次旅行的方式。

          这里是一个带有测试用例的 Java 实现的链接:

          http://stones333.blogspot.com/2013/12/find-cycles-in-directed-graph-dag.html

          【讨论】:

          • DAG(有向无环图)中如何存在循环?
          • 这并没有找到所有的循环。
          【解决方案10】:

          无向图的情况下,最近发表的一篇论文(无向图中循环和 st 路径的优化列表)提供了渐近最优解。你可以在这里阅读它http://arxiv.org/abs/1205.2766 或在这里http://dl.acm.org/citation.cfm?id=2627951 我知道它没有回答您的问题,但由于您的问题的标题没有提及方向,它可能仍然对 Google 搜索有用

          【讨论】:

            【解决方案11】:

            从起始节点s开始DFS,在遍历过程中跟踪DFS路径,如果在到s的路径中找到节点v的边,则记录该路径。 (v,s) 是 DFS 树中的后沿,因此表示包含 s 的循环。

            【讨论】:

            • 很好,但这不是 OP 想要的:找到所有周期,可能是最小的。
            【解决方案12】:

            澄清一下:

            1. 强连通组件将查找其中至少有一个循环的所有子图,而不是图中所有可能的循环。例如如果您采用所有强连接组件并将它们中的每一个折叠/分组/合并到一个节点(即每个组件一个节点),您将得到一棵没有循环的树(实际上是一个 DAG)。每个组件(基本上是一个包含至少一个循环的子图)内部可以包含更多可能的循环,因此 SCC 不会找到所有可能的循环,它会找到所有可能的组,至少有一个循环,如果你分组他们,那么图形将没有循环。

            2. 要在图中找到 所有 个简单循环,正如其他人所提到的,Johnson 算法是一种候选算法。

            【讨论】:

              【解决方案13】:

              关于您对排列循环的问题,请在此处阅读更多信息: https://www.codechef.com/problems/PCYCLE

              你可以试试这个代码(输入大小和位数):

              # include<cstdio>
              using namespace std;
              
              int main()
              {
                  int n;
                  scanf("%d",&n);
              
                  int num[1000];
                  int visited[1000]={0};
                  int vindex[2000];
                  for(int i=1;i<=n;i++)
                      scanf("%d",&num[i]);
              
                  int t_visited=0;
                  int cycles=0;
                  int start=0, index;
              
                  while(t_visited < n)
                  {
                      for(int i=1;i<=n;i++)
                      {
                          if(visited[i]==0)
                          {
                              vindex[start]=i;
                              visited[i]=1;
                              t_visited++;
                              index=start;
                              break;
                          }
                      }
                      while(true)
                      {
                          index++;
                          vindex[index]=num[vindex[index-1]];
              
                          if(vindex[index]==vindex[start])
                              break;
                          visited[vindex[index]]=1;
                          t_visited++;
                      }
                      vindex[++index]=0;
                      start=index+1;
                      cycles++;
                  }
              
                  printf("%d\n",cycles,vindex[0]);
              
                  for(int i=0;i<(n+2*cycles);i++)
                  {
                      if(vindex[i]==0)
                          printf("\n");
                      else
                          printf("%d ",vindex[i]);
                  }
              }
              

              【讨论】:

                【解决方案14】:

                我发现解决此问题的最简单选择是使用名为 networkx 的 python 库。

                它实现了这个问题的最佳答案中提到的约翰逊算法,但执行起来非常简单。

                简而言之,您需要以下内容:

                import networkx as nx
                import matplotlib.pyplot as plt
                
                # Create Directed Graph
                G=nx.DiGraph()
                
                # Add a list of nodes:
                G.add_nodes_from(["a","b","c","d","e"])
                
                # Add a list of edges:
                G.add_edges_from([("a","b"),("b","c"), ("c","a"), ("b","d"), ("d","e"), ("e","a")])
                
                #Return a list of cycles described as a list o nodes
                list(nx.simple_cycles(G))
                

                答案: [['a', 'b', 'd', 'e'], ['a', 'b', 'c']]

                【讨论】:

                • 您还可以将字典转换为 networkx 图:nx.DiGraph({'a': ['b'], 'b': ['c','d'], 'c': ['a'], 'd': ['e'], 'e':['a']})
                • 如何指定起始顶点?
                【解决方案15】:

                具有后边缘的基于 DFS 的变体确实会找到循环,但在许多情况下它不会是 minimal 循环。一般来说,DFS 会给你一个存在循环的标志,但它还不足以真正找到循环。例如,想象 5 个不同的循环共享两条边。仅使用 DFS(包括回溯变体)没有简单的方法来识别循环。

                约翰逊算法确实给出了所有唯一的简单循环,并且具有良好的时间和空间复杂度。

                但是,如果您只想找到 MINIMAL 循环(这意味着可能有不止一个循环通过任何顶点,我们有兴趣找到最小的循环)并且您的图不是很大,您可以尝试使用简单的下面的方法。 与约翰逊的相比,它非常简单但相当缓慢。

                因此,绝对找到 MINIMAL 环的最简单方法之一是使用 Floyd 算法通过邻接矩阵找到所有顶点之间的最小路径。 这个算法远没有 Johnson 算法那么最优,但它非常简单,而且它的内部循环非常紧凑,以至于对于较小的图(

                  val NO_EDGE = Integer.MAX_VALUE / 2
                
                  def shortestPath(weights: Array[Array[Int]]) = {
                    for (k <- weights.indices;
                         i <- weights.indices;
                         j <- weights.indices) {
                      val throughK = weights(i)(k) + weights(k)(j)
                      if (throughK < weights(i)(j)) {
                        weights(i)(j) = throughK
                      }
                    }
                  }
                

                最初,该算法在加权边图上运行,以找到所有节点对之间的所有最短路径(因此使用权重参数)。为了使其正常工作,如果节点之间存在有向边,则需要提供 1 ,否则提供 NO_EDGE 。 算法执行后,可以检查主对角线,如果有小于 NO_EDGE 的值,则该节点参与长度等于该值的循环。同一循环的每个其他节点都将具有相同的值(在主对角线上)。

                要重建循环本身,我们需要使用稍微修改过的带有父跟踪的算法版本。

                  def shortestPath(weights: Array[Array[Int]], parents: Array[Array[Int]]) = {
                    for (k <- weights.indices;
                         i <- weights.indices;
                         j <- weights.indices) {
                      val throughK = weights(i)(k) + weights(k)(j)
                      if (throughK < weights(i)(j)) {
                        parents(i)(j) = k
                        weights(i)(j) = throughK
                      }
                    }
                  }
                

                如果顶点之间存在边,则父矩阵最初应在边单元中包含源顶点索引,否则为 -1。 函数返回后,对于每条边,您都将引用最短路径树中的父节点。 然后很容易恢复实际周期。

                总而言之,我们有以下程序来查找所有最小循环

                  val NO_EDGE = Integer.MAX_VALUE / 2;
                
                  def shortestPathWithParentTracking(
                         weights: Array[Array[Int]],
                         parents: Array[Array[Int]]) = {
                    for (k <- weights.indices;
                         i <- weights.indices;
                         j <- weights.indices) {
                      val throughK = weights(i)(k) + weights(k)(j)
                      if (throughK < weights(i)(j)) {
                        parents(i)(j) = parents(i)(k)
                        weights(i)(j) = throughK
                      }
                    }
                  }
                
                  def recoverCycles(
                         cycleNodes: Seq[Int], 
                         parents: Array[Array[Int]]): Set[Seq[Int]] = {
                    val res = new mutable.HashSet[Seq[Int]]()
                    for (node <- cycleNodes) {
                      var cycle = new mutable.ArrayBuffer[Int]()
                      cycle += node
                      var other = parents(node)(node)
                      do {
                        cycle += other
                        other = parents(other)(node)
                      } while(other != node)
                      res += cycle.sorted
                    }
                    res.toSet
                  }
                

                还有一个小 main 方法只是为了测试结果

                  def main(args: Array[String]): Unit = {
                    val n = 3
                    val weights = Array(Array(NO_EDGE, 1, NO_EDGE), Array(NO_EDGE, NO_EDGE, 1), Array(1, NO_EDGE, NO_EDGE))
                    val parents = Array(Array(-1, 1, -1), Array(-1, -1, 2), Array(0, -1, -1))
                    shortestPathWithParentTracking(weights, parents)
                    val cycleNodes = parents.indices.filter(i => parents(i)(i) < NO_EDGE)
                    val cycles: Set[Seq[Int]] = recoverCycles(cycleNodes, parents)
                    println("The following minimal cycle found:")
                    cycles.foreach(c => println(c.mkString))
                    println(s"Total: ${cycles.size} cycle found")
                  }
                

                输出是

                The following minimal cycle found:
                012
                Total: 1 cycle found
                

                【讨论】:

                  【解决方案16】:

                  二楼回答中伪代码的DFS c++版本:

                  void findCircleUnit(int start, int v, bool* visited, vector<int>& path) {
                      if(visited[v]) {
                          if(v == start) {
                              for(auto c : path)
                                  cout << c << " ";
                              cout << endl;
                              return;
                          }
                          else 
                              return;
                      }
                      visited[v] = true;
                      path.push_back(v);
                      for(auto i : G[v])
                          findCircleUnit(start, i, visited, path);
                      visited[v] = false;
                      path.pop_back();
                  }
                  

                  【讨论】:

                    【解决方案17】:

                    CXXGraph提供了一组算法和函数来检测循环。

                    如需完整的算法解释,请访问wiki

                    【讨论】:

                      猜你喜欢
                      • 1970-01-01
                      • 2014-01-02
                      • 1970-01-01
                      • 2018-05-15
                      • 1970-01-01
                      • 1970-01-01
                      • 1970-01-01
                      • 2015-12-25
                      • 1970-01-01
                      相关资源
                      最近更新 更多