簡體   English   中英

有向循環圖的最長路徑

[英]Longest Path for Directed Cyclic Graph

使用哪種算法通過有向循環未加權圖找到最長路徑。 每個節點僅指向另一個節點。 這些圖最多包含10 ^ 9個節點。 (我在這里搜索,谷歌搜索都無濟於事。)

請參閱烏龜和野兔回路檢測 -您發送一個迭代器,其步長為1(烏龜),另一個發送迭代器,其步長為2(野兔)。 如果列表中有一個循環,它們必然會相遇。 另外一個關於SO的問題 )。

  1. 取一個節點
  2. 開始“烏龜和野兔”,標記烏龜訪問的節點(O(N)空間復雜度,布爾數組就足夠了)
  3. 一旦烏龜遇到了野兔, 一旦野兔踩到了已經被烏龜訪問過的節點上(即它們都在一個環內),就停止野兔,將烏龜帶到與野兔相同的位置,然后讓烏龜繞環繞行再次計算循環的長度 (以檢查到目前為止的最大值)

  4. 采取另一個尚未訪問的節點,然后轉到步驟2


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”(循環)

因此,您沒有一個圖,而是有一系列不同的圖,每個圖形成一個具有不同數量節點的閉合鏈。

在這種情況下,您可以實現一種算法,該算法大致使用O(n)時間復雜度和O(n)空間復雜度(假設對所有節點的隨機訪問,並且每個節點的ID都遞增)。

從第一個節點開始,並遍歷鏈,直到您回到第一個節點。 對於每個訪問的節點,請使用鏈標識符對其進行標記。 存儲此鏈ID的訪問節點數。 然后,轉到下一個節點(按ID,不在鏈中),檢查它是否已被標記為鏈的一部分。 如果是,請繼續; 如果沒有,請處理鏈條。 這樣做,直到找到最后一個節點ID。 至此,您已經知道了所有鏈的長度,然后選擇了最長的鏈。

首先以遞歸方式刪除度內零的每個頂點(在O(n)中)。 結果圖只是循環的不相交的並集。

取得任意節點,運行dfs,並找到其所屬循環的長度(僅通過訪問鄰居即可,這是自然的dfs)。 對每個未訪問的節點繼續執行此操作。 最后,您可以輸出最大的循環。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM