單鏈表是否有環並如何找到環入口


 1、如何判斷一個鏈表是不是有環?   

2、如果鏈表為存在環,如果找到環的入口點?


這個算是一個比較老的題目了,之前就看到過,一般通用的做法就是弄兩個指針,一個走得快一點,一個走得慢一點。一般是弄一個走一步,一個走兩步。這樣如果他們相遇,則說明有環。

那么在有環的基礎上,怎么找到這個環的入口呢,一般網上也會給出解釋,可能是我的理解力比較底,網上的解釋中,總是用移動

了s步,又是長度的,總是弄的我很暈,於是,給出我自己的解釋好了,所有的都用移動了多少步來說明。

走一步的指針叫slow,走兩步的叫fast。

相遇的時候,slow共移動了s步,fast共移動了2s步,這個是顯而易見的。

定義a如下: 鏈表頭移動a步到達入口點。

定義x如下: 入口點移動x步到達相遇點。

定義r如下: 從環中的某一點移動r步,又到達的這一點,也就是轉了一圈的意思。

定義t如下: 從相遇點移動到入口點的移動步數

定義L如下: 從鏈表頭移動L步,又到達了相遇點。也就是遍歷完鏈表之后,通過最后一個節點的指針,又移動到了鏈表中的某一點。

其中L = a + r  =  a + x + t

那么slow和fast相遇了,fast必然比slow多走了n個圈,也就是 n*r 步,那么

s = a + x   

2s = s + n*r , 可得  s = n*r

將s=a+x,帶入s =n*r,可得 a+x = n*r, 也就是 a+x = (n-1)*r + r   

從表頭移動到入口點,再從入口點移動到入口點,也就是移動了整個鏈表的距離,即是 L =  a + r , 所以r = L - a

所以   a+x = (n-1)*r + L - a ,   於是 a  = (n-1)*r + L - a - x = (n-1)*r + t

也就是說,從表頭到入口點的距離,等於從相遇點繼續遍歷又到表頭的距離。

所以,從表頭設立一個指針,從相遇點設立一個指針,兩個同時移動,必然能夠在入口點相遇,這樣,就求出了相遇點。

代碼如下:

typedef struct node{
    int elem;
    struct node * next;
}Node, *NodeList;

//尋找環的入口點
NodeList FindLoopPort(NodeList head)
{
    NodeList slow=head,fast=head;
    //得到相遇點
    while(fast && fast->next)
    {
        slow=slow->next;
        fast=fast->next->next;
        if(slow==fast)
            break;
    }
    if(fast==NULL||fast->next==NULL)
        return NULL;
    //slow指向開頭,fast在相遇點
    //得到入口點
    slow=head;
    while(slow!=fast){
        slow=slow->next;
        fast=fast->next;
    }
    return slow;
}

 


於是我又思考了一下,對於一個帶環的鏈表如何遍歷輸出所有節點的值,因為不能夠通過最后移動到NULL來判斷結束。

如果鏈表設置了尾節點(環入口點)的話,就通過比較是不是又到達了尾節點就可以作為結束條件了

如果沒有設置尾節點的話,那么在遍歷的時候,把每個節點的地址放入一個set集合中,如果下一個地址在set中出現了,那么也就結束了。

不知道有木有其他的方法了。

 

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM