有向循环图的最长路径

时间:2016-12-12 09:41:08

标签: algorithm graph

使用什么算法通过有向循环未加权图找到最长路径。每个节点仅指向另一个节点。这些图表最多有10 ^ 9个节点。 (我在这里搜索并谷歌无济于事。)

3 个答案:

答案 0 :(得分:0)

参见the tortoise and hare loop detection - 你发送一个步长为1(乌龟)的迭代器和另一个步长为2(野兔)的迭代器。如果列表有一个循环,它们必然会遇到。 (也在another question on SO)。

  1. 拿一个节点
  2. 启动“乌龟与兔子”,标记乌龟所访问的节点(O(N)空间复杂度,bool数组就足够了)
  3. 一旦乌龟遇到野兔,一旦兔子走到乌龟已经访问过的节点上(即两者都在一个环路内),停止野兔,将乌龟带到与野兔相同的位置,让乌龟再次环绕循环并计算循环的长度(以检查到目前为止的最大值)

  4. 转到另一个尚未访问过的节点,然后转到第2步

  5. C ++解决方案

    size_t maxLoopLen(const std::vector<size_t>& nextNodeIndexes) {
      size_t len=nextNodeIndexes.size();
      std::vector<bool> visitTrace(len, false);
      size_t ret=0; // the max number of elements in the loop
      for(;;) {
        // find the first non-visited node
        size_t pos=0;
        for(pos=0; pos<len && visitTrace[pos]; pos++);
        if(pos>=len) { // no more unvisited nodes
          break;
        }
    
        // this is needed for the "ring with string attached" topology
        // The global visitTrace contains the exploration of the prev
        // loos or **string leading to the same loop** - if the hare
        // steps on one of those prev strings, it may stop prematurely
        // (on the string, not inside the loop)
        std::vector<bool> currCycleTrace(len, false);
    
        size_t hare=pos, tortoise=pos;
        bool hareOnKnownPosition=false;
        while ( !currCycleTrace[hare] && !hareOnKnownPosition) {
          if(visitTrace[hare]) {
            // the hare just got to revisit something visited on prev cycles
            // *** ***********************************************************
            // *** this is where the algo achieves sub-O(N^2) time complexity
            // *** ***********************************************************
            hareOnKnownPosition=true;
            break;
          }
          // mark the tortoise pos as visited
          visitTrace[tortoise]=currCycleTrace[tortoise]=true;
          // tortoise steps with increment of one
          tortoise=nextNodeIndexes[tortoise];
          // hare steps two
          hare=nextNodeIndexes[hare];
          hare=nextNodeIndexes[hare];
        }
        // we got out of that cycle because the hare stepped on either:
        // - a tortoise-visited place on the current cycle - in this case
        //   both the tortoise and the hare are inside a not-yet-explored
        //   loop.
        // - on a place where the tortoise has been when it discovered a
        //   loop at prev cycles (think "ring with multiple string attached"
    
        if(!hareOnKnownPosition) {
          // The hare stepped on a new loop, not a loop visited before
    
          // Bring the tortoise to the same position as the hare. keep the
          // hare still and start counting how many steps until the tortoise
          // gets back to the same place
          tortoise=hare;
          size_t currLoopElemCount=0;
          do {
            tortoise=nextNodeIndexes[tortoise];
            currLoopElemCount++;
          } while(tortoise!=hare);
          ret=std::max(currLoopElemCount, ret);
        }
      }
      return ret;
    }
    
    #include <iostream>
    int main() {
      std::vector<size_t> lasso={3,3,1,2,0};
      // expected 3, with cycle at nodes at indexes 1,2,3
      std::cout << "lasso max loop len " << maxLoopLen(lasso) << std::endl;
    
      // expected 2. The ring index 1 and 2. Two connected strings
      // - starting at index 0 - 0->3->2 and we are inside the ring
      // - starting at index 4 - 4->1  and we are inside the ring
      std::vector<size_t> ringWith2Strings={3,2,1,2,1};
      std::cout << "ringWith2Strings max loop len " 
                << maxLoopLen(ringWith2Strings) << std::endl;
    
      std::vector<size_t> singleElem={0};
      std::cout << "singleElem max loop len " << maxLoopLen(singleElem) << std::endl;
    
      std::vector<size_t> allTogether={
        3,3,1,2,0, // lasso
        8,7,6,7,6, // ringWith2Strings shifted up 5 pos
        10 // single element pointing to itself
      };
      std::cout << "allTogether max loop len " << maxLoopLen(allTogether) << std::endl;
    }
    

    探索“节点”的例子

    lasso={3,3,1,2,0};
    
    • 节点4说“转到节点0”
    • 节点0表示“转到节点3”
    • 节点3说“转到节点2”
    • 节点2说“转到节点1”
    • 节点1表示“转到节点3”(循环)

答案 1 :(得分:-1)

所以你没有一个单独的图,而是一系列不同的图,每个图形成一个具有不同数量节点的闭链。

如果是这种情况,您可以实现一种大致使用O(n)时间复杂度和O(n)空间复杂度的算法(假设随机访问所有节点,并且每个节点都有一个升序ID)。 / p>

从第一个节点开始,遍历链,直到回到第一个节点。对于每个访问节点,使用链标识符对其进行标记。存储此链ID的访问节点数。然后转到下一个节点(按ID,不在链中),检查它是否已被标记为链的一部分。如果是,请继续;如果没有,处理链。在您使用最后一个节点ID之前,请执行此操作。此时你已经完成了,你知道所有链的长度,然后你选择最长的链。

答案 2 :(得分:-1)

首先递归地移除度为零的每个顶点(在O(n)中)。结果图只是一个不相交的循环联合。

获取任意节点,运行dfs,并找到它所属的循环的长度(仅通过访问邻居,一个自然的dfs)。对每个未访问的节点继续此操作。最后,您可以输出最大的周期。