如何檢查一個單向鏈表上是否有環?

來源:互聯網
上載者:User

1, 最簡單的方法, 用一個指標遍曆鏈表, 每遇到一個節點就把他的記憶體位址(java中可以用object.hashcode())做為key放在一個hashtable中. 這樣當hashtable中出現重複key的時候說明此鏈表上有環. 這個方法的時間複雜度為O(n), 空間同樣為O(n). 

2, 使用反轉指標的方法, 每過一個節點就把該節點的指標反向:

  Boolean reverse(Node *head) {

   Node *curr = head;

   Node *next = head->next;

   curr->next = NULL;

   while(next!=NULL) {

    if(next == head) { /* go back to the head of the list, so there is a loop */

      next->next = curr;

      return TRUE;

    }

    Node *temp = curr;

    curr = next;

    next = next->next;

    curr->next = temp;

   }

   /* at the end of list, so there is no loop, let's reverse the list back */

   next = curr->next;

   curr ->next = NULL;

   while(next!=NULL) {

    Node *temp = curr;

    curr = next;

    next = next->next;

    curr->next = temp;

   }

   return FALSE;

  }

看上去這是一種奇怪的方法: 當有環的時候反轉next指標會最終走到鏈表頭部; 當沒有環的時候反轉next指標會破壞鏈表結構(使鏈表反向), 所以需要最後把鏈表再反向一次. 這種方法的空間複雜度是O(1), 實事上我們使用了3個額外指標;而時間複雜度是O(n), 我們最多2次遍曆整個鏈表(當鏈表中沒有環的時候).

這個方法的最大缺點是在多線程情況下不安全, 當多個線程都在讀這個鏈表的時候, 檢查環的線程會改變鏈表的狀態, 雖然最後我們恢複了鏈表本身的結構, 但是不能保證其他線程能得到正確的結果.


3, 這是一般面試官所預期的答案: 快指標和慢指標

Boolean has_loop(Node *head) {

  Node *pf = head; /* fast pointer */

  Node *ps = head; /* slow pointer */

  while(true) {

    if(pf && pf->next)

      pf = pf->next->next;

    else

      return FALSE;

    ps = ps->next;

    if(ps == pf)

      return TRUE;

  }

}

需要說明的是, 當慢指標(ps)進入環之後, 最多會走n-1步就能和快指標(pf)相遇, 其中n是環的長度. 也就是說快指標在環能不會跳過慢指標, 這個性質可以簡單的用歸納法來證明. (1)當ps在環中位置i, 而pf在環中位置i-1, 則在下一個iteration, ps會和pf在i+1相遇.
(2)當ps在環中位置i, 而pf在環中位置i-2, 則在下一個iteration, ps在i+1, pf在i, 於是在下一個iteration ps和pf會相遇在i+2位置

(3)和上面推理過程類似, 當ps在i, pf在i+1, 則他們會經過n-1個iteration在i+n-1的位置相遇. 於是慢指標的步數不會超過n-1.

擴充:

這個問題還有一些擴充, 例如, 如何找到環的開始節點? 如何解開這個環? 這些問題的本質就是如何找到有"回邊"的那個節點.

我們可以利用方法3的一個變形來解決這個問題:

 

Boolean has_loop(Node *head) {

  Node *pf = head; /* fast pointer */

  Node *ps = head; /* slow pointer */

  while(true) {  /* step 1, is there a loop? */

    if(pf && pf->next)

      pf = pf->next->next;

    else

      return FALSE;

    ps = ps->next;

    if(ps == pf)

      break;

  }

  /* step 2, how long is the loop */

  int i = 0;

   do {

    ps = ps->next;

    pf = pf->next->next;

    i++;

  } while(ps!=pf)

 

  /* step 3, use 2 addtional pointers with distance i to break the loop */

  ps = head;

  pf = head;

  int j;

  for(j=0; j<i; j++) { pf = pf->next; }

  j = 0;

  while(ps!=pf) {

   ps = ps->next;

   pf = pf->next;

   j++;

  }

  printf("loop begins at position %d, node address is %x", j, ps);

  /*step 4, break the loop*/

  for(j=0; j<=i; j++) { ps = ps->next; } //step i-1 in the loop from loop beginning

  ps->next = NULL; // break the loop

 

  return TRUE;

}

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.