如何判斷單向鏈表是否帶環,有迴環問題。

問題描述:

一個單向鏈表的結構,其中一個節點通常爲保存一個數據的容器object(data)和一個指向下一個保存數據的容器的地址next組成。

 什麼叫帶環,或者有迴環呢?

比如當前節點next指向當前節點之前任意一個節點地址,那麼我們說這個單向鏈表有迴環。或者帶環。

1.那麼如何判斷一個單向鏈表有環呢?

雖然網上有很多博客都說教式的闡述瞭如何判斷是否有環,但我覺得大多都寫得很爛。所以我決定自己寫一篇。讓自己深刻一遍。

我們先思考怎樣纔算有環,或者有環的條件

我們大腦裏要有一個有環的單向鏈表結構。這裏我們看當一個單向鏈表中只要有兩個節點的next指向同一個節點的地址時,是不是就是有環了。如何理解這句話,如下圖,綠色線代表一個迴環P節點連接點,我們看是不是a1和a2指向同一個P節點時,這個單向鏈表就是有迴環的。

 當我們找出了問題的根本,再來看其他的博客時,就會好理解得多了。

2.爲什麼其他博客中會說到一個走的快的指針和一個走的慢的指針,當他們指向同一個節點時,就代表這個單向鏈表有環了?

操場跑道是一個環對吧。當從學校宿舍到跑道的路程當做還未進入環的鏈表節點,跑道就是鏈表的迴環,一個跑的快,一個跑的慢,跑的慢的將永遠追不上跑的慢的,但是一旦進入的跑道,或者鏈表迴環,那麼一旦跑的快的超過慢的一個迴環的路程後,他們就相遇了。這就能證明這個鏈表有迴環。速度不同,相遇<===>有迴環  , 速度不同,不相遇<===>無迴環。因爲我們先確定兩個指針速度不同,那麼根據是否相遇 就能確定是否有迴環了。這是結論。

3.重中之重!爲什麼要規定慢的走一步,而快的走兩步?而不是慢的一步,快的三步,或者其他?

這算是判斷鏈表是否有環的問題最重要的一個規則了,百分之99的博客只說了結慢的走一步,而快的走兩步,並未說爲什麼,這裏我們來思考爲什麼。如下圖

 有6個節點的一個迴環。A快指針每次走3步,B指針每次都一步。

當B慢指針進入迴環的1位置時,A指針在4位置。

當B慢指針在2位置時,A快指針在1位置。(這就是上圖示例的位置)

當B慢指針在3位置時,A快指針在4位置。

當B慢指針在4位置時,A快指針在1位置。

當B慢指針在5位置時,A快指針在4位置。

當B慢指針在6位置時,A快指針在1位置。

當B慢指針在1位置時,A快指針在4位置。(此時已經和剛進入圈的時候位置一樣了)

當B慢指針在2位置時,A快指針在1位置。

當B慢指針在3位置時,A快指針在4位置。

如果你細心發現他們永遠不會相遇,A與B指針永遠不會在同一個位置。

那麼爲什麼呢???

我們還是來思考兩個指針走過的節點數相差的節點數量。是不是隻有兩個指針相差0個節點,6個節點,12個節點,18個....節點的時候他們纔會相遇。想象操場,快的比慢的超過1圈的距離,2圈的距離,3圈的距離時,纔會相遇。那麼如果快的和慢的一個走3步一個走一步,如果開始位置不同,一個1一個2,那麼他們永遠是(2,5,)(3,8)(4,11)(5,14)(6,17)

相差的數量爲:3,5,7,9,11,13,14....大於6的減去迴環6,那麼他們永遠相差3,5,1,3,5,1.....永遠不會相差6的倍數個。

4.那麼快的走2步,慢的走1步有什麼不同?

還是從相差的節點數量來考慮。不論開始相差幾個,比如開始進入迴環的時候,一個是1一個是5,

那麼情況是(1,5,)(2,7)(3,9)(4,11)(5,13)...

相差位置爲4,5,6,7,8.....

相信聰明的你已經知道爲什麼要求快的走2步,慢的走一步了?因爲只有這樣,不論你的迴環是多少節點組成,兩個節點相差的距離中間只要不漏掉任何一個相差數額,最後總會等於迴環的節點數,而當相差的距離等於迴環數的時候就是他們相遇的時刻。就是快的超過慢的一圈或者n圈的時刻。

這就是爲什麼要求快的走2步,慢的走1步的原因。

其他那些博客寫的啥玩意,看也看不懂一堆公式扔給你也不告訴你爲什麼。氣     _(:з」∠)_

既然知道里原理,實現代碼就好寫了。

Java代碼實現:

public class test{
    public boolean hasCycle(ListNode head){
        ListNode slow,fast;//定義快慢指針
        slow=fast=head;//同一起跑線
        while(fast!=null&&fast.next!=null){//當有指針指向null時,說明到尾結點了,說明此鏈表無環
            slow=slow.next;//走一步
            fast=fast.next.next;//走兩步
            if(fast==slow){//相遇
                return ture;
            }
        }
        return false;
    }
}

//單向鏈表的一個節點
public class ListNode{
    int value;//博客裏說裝數據的地方
    ListNode next;//下一個節點的地址
    ListNode(int x){//本節點構造器
        value=x;//頭節點
        next=null;
    }
}

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章