判斷鏈表中是否有環 ----- 有關單鏈表中環的問題

 轉自:https://www.cnblogs.com/dancingrain/p/3405197.htmlhtml

給定一個單鏈表,判斷其中是否有環,已是一個比較老同時也是比較經典的問題,在網上搜集了一些資料,node

而後總結一下大概能夠涉及到的問題,以及相應的解法。oop

 

首先,關於單鏈表中的環,通常涉及到一下問題:測試

1.給一個單鏈表,判斷其中是否有環的存在;.net

2.若是存在環,找出環的入口點;指針

3.若是存在環,求出環上節點的個數;htm

4.若是存在環,求出鏈表的長度;blog

5.若是存在環,求出環上距離任意一個節點最遠的點(對面節點);ci

6.(擴展)如何判斷兩個無環鏈表是否相交;get

7.(擴展)若是相交,求出第一個相交的節點;

 

下面,我將針對上面這七個問題一一給出解釋和相應的代碼。

1.判斷時候有環(鏈表頭指針爲head)

對於這個問題咱們能夠採用「快慢指針」的方法。就是有兩個指針fast和slow,開始的時候兩個指針都指向鏈表頭head,而後在每一步

操做中slow向前走一步即:slow = slow->next,而fast每一步向前兩步即:fast = fast->next->next。

因爲fast要比slow移動的快,若是有環,fast必定會先進入環,而slow後進入環。當兩個指針都進入環以後,通過必定步的操做以後

兩者必定可以在環上相遇,而且此時slow尚未繞環一圈,也就是說必定是在slow走完第一圈以前相遇。證實能夠看下圖:

當slow剛進入環時每一個指針可能處於上面的狀況,接下來slow和fast分別向前走即:

if (slow != NULL && fast->next != NULL)  
{  
         slow = slow -> next ;  
         fast = fast -> next -> next ;  
}  

  也就是說,slow每次向前走一步,fast向前追了兩步,所以每一步操做後fast到slow的距離縮短了1步,這樣繼續下去就會使得

 

二者之間的距離逐漸縮小:...、五、四、三、二、一、0 -> 相遇。又由於在同一個環中fast和slow之間的距離不會大於換的長度,所以

到兩者相遇的時候slow必定尚未走完一週(或者正好走完之後,這種狀況出如今開始的時候fast和slow都在環的入口處)。

typedef struct node{  
    char data ;  
    node * next ;  
}Node;  
bool exitLoop(Node *head)  
{  
    Node *fast, *slow ;  
    slow = fast = head ;  
  
    while (slow != NULL && fast -> next != NULL)  
    {  
        slow = slow -> next ;  
        fast = fast -> next -> next ;  
        if (slow == fast)  
            return true ;  
    }  
    return false ;  
}  

  下面看問題2,找出環的入口點: 

  我結合着下圖講解一下:

從上面的分析知道,當fast和slow相遇時,slow尚未走完鏈表,假設fast已經在環內循環了n(1<= n)圈。假設slow走了s步,則fast走了2s步,又因爲

fast走過的步數 = s + n*r(s + 在環上多走的n圈),則有下面的等式:

 

2*s = s + n  * r ; (1)

 => s = n*r (2)

若是假設整個鏈表的長度是L,入口和相遇點的距離是x(如上圖所示),起點到入口點的距離是a(如上圖所示),則有:

a + x = s = n * r; (3)  由(2)推出

a + x = (n - 1) * r + r  = (n - 1) * r + (L - a) (4) 由環的長度 = 鏈表總長度 - 起點到入口點的距離求出

a = (n - 1) * r + (L -a -x) (5)

 

集合式子(5)以及上圖咱們能夠看出,從鏈表起點head開始到入口點的距離a,與從slow和fast的相遇點(如圖)到入口點的距離相等。

所以咱們就能夠分別用一個指針(ptr1, prt2),同時從head與slow和fast的相遇點出發,每一次操做走一步,直到ptr1 == ptr2,此時的位置也就是入口點!

到此第二個問題也已經解決。

下面給出示意性的簡單代碼(沒有測試可是應該沒有問題):

Node* findLoopStart(Node *head)  
{  
    Node *fast, *slow ;  
    slow = fast = head ;  
  
    while (slow != NULL && fast -> next != NULL)  
    {  
        slow = slow -> next ;  
        fast = fast -> next -> next ;  
        if (slow == fast) break ;  
    }  
    if (slow == NULL || fast -> next == NULL) return NULL ; //沒有環,返回NULL值  
  
    Node * ptr1 = head ; //鏈表開始點  
    Node * ptr2 = slow ; //相遇點  
    while (ptr1 != ptr2)   
    {  
        ptr1 = ptr1 -> next ;  
        ptr2 = ptr2 -> next ;  
    }  
    return ptr1 ; //找到入口點  
}  

  

第3個問題,若是存在環,求環上節點的個數:

對於這個問題,我這裏有兩個思路(確定還有其它跟好的辦法):

思路1:記錄下相遇節點存入臨時變量tempPtr,而後讓slow(或者fast,都同樣)繼續向前走slow = slow -> next;一直到slow == tempPtr; 此時通過的步數就是環上節點的個數;

思路2: 從相遇點開始slow和fast繼續按照原來的方式向前走slow = slow -> next; fast = fast -> next -> next;直到兩者再次項目,此時通過的步數就是環上節點的個數 。

 

第一種思路很簡單,其實就是一次遍歷鏈表的環,從而統計出點的個數,沒有什麼能夠詳細解釋的了。

對於第二種思路,咱們能夠這樣想,結合上面的分析,fast和slow沒一次操做都會使得二者之間的距離較少1。咱們能夠把二者相遇的時候看作二者之間的距離正好是整個環的

長度r。所以,當再次相遇的時候所通過的步數正好是環上節點的數目。

因爲這兩種思路都比較簡單,代碼也很容易實現,這裏就不給出了。

 

問題4是若是存在環,求出鏈表的長度:

到這裏,問題已經簡單的多了,由於咱們在問題一、二、3中已經作得足夠的」準備工做「。

咱們能夠這樣求出整個鏈表的長度:

 

鏈表長度L = 起點到入口點的距離 + 環的長度r;

 

已經知道了起點和入口點的位置,那麼二者之間的距離很好求了吧!環的長度也已經知道了,所以該問題也就迎刃而解了!

 

問題5是,求出環上距離任意一個節點最遠的點(對面節點)

 

以下圖所示,點1和四、點2和五、點3和6分別互爲」對面節點「 ,也就是換上最遠的點,咱們的要求是怎麼求出換上任意一個點的最遠點。

對於換上任意的一個點ptr0, 咱們要找到它的」對面點「,能夠這樣思考:一樣使用上面的快慢指針的方法,讓slow和fast都指向ptr0,每一步都執行與上面相同的操做(slow每次跳一步,fast每次跳兩步),

當fast = ptr0或者fast = prt0->next的時候slow所指向的節點就是ptr0的」對面節點「。

爲何是這樣呢?咱們能夠這樣分析:

 

 

如上圖,咱們想像一下,把環從ptro處展開,展開後能夠是無限長的(如上在6後重復前面的內容)如上圖。

如今問題就簡單了,因爲slow移動的距離永遠是fast的通常,所以當fast遍歷玩整個環長度r個節點的時候slow正好遍歷了r/2個節點,

也就是說,此時正好指向距離ptr0最遠的點。

 

對於問題6(擴展)如何判斷兩個無環鏈表是否相交,和7(擴展)若是相交,求出第一個相交的節點,其實就是作一個問題的轉化:

 

假設有連個鏈表listA和listB,若是兩個鏈表都無環,而且有交點,那麼咱們可讓其中一個鏈表(不妨設是listA)的爲節點鏈接到其頭部,這樣在listB中就必定會出現一個環。

所以咱們將問題6和7分別轉化成了問題1和2.

看看下圖就會明白了:

到此結束!休息一下!

參考:

http://www.cppblog.com/humanchao/archive/2012/11/12/47357.html

http://blog.csdn.net/liuxialong/article/details/6555850

http://blog.csdn.net/liuxialong/article/details/6555850

歡迎轉載,轉載請註明原地址:http://blog.csdn.net/doufei_ccst/article/details/10578315

相關文章
相關標籤/搜索