連結串列中環的入口結點(劍指offer56題)
阿新 • • 發佈:2018-12-15
一、題目描述
給一個連結串列,若其中包含環,請找出該連結串列的環的入口結點,否則,輸出null。
二、解題思路
方法一、利用set的add方法或list的contains方法。但是list 的contains方法時間複雜度為n^2,儘量用set的add方法,時間複雜度為O(1)
方法二、利用快慢指標思想(推薦) 時間複雜度為O(n),空間複雜度為O(1)
證明:
假設x為環前面的路程(黑色路程),a為環入口到相遇點的路程(藍色路程,假設順時針走), c為環的長度(藍色+橙色路程)
當快慢指標相遇的時候: slow為慢指標一次走一個結點,fast為快指標一次走兩個結點。
此時慢指標走的路程為slow = x + m * c + a (n,m為環的圈數)
快指標走的路程為fast = x + n * c + a
2 slow = fast (同時開始走,同一時刻fast的路程是slow的兩倍)
2 * ( x + m*c + a ) = (x + n *c + a)
從而可以推匯出:
x = (n - 2 * m )*c - a
= (n - 2 *m -1 )*c + c - a
即環前面的路程 = 數個環的長度(為可能為0) + c - a
什麼是c - a?這是相遇點後,環後面部分的路程。(橙色路程)
所以,我們可以讓一個指標從起點A開始走,讓一個指標從相遇點B開始繼續往後走,
2個指標速度一樣,那麼,當從原點的指標走到環入口點的時候(此時剛好走了x)
從相遇點開始走的那個指標也一定剛好到達環入口點。
所以2者會相遇,且恰好相遇在環的入口點。
最後,判斷是否有環,且找環的演算法複雜度為:
時間複雜度:O(n)
空間複雜度:O(1)
三、java程式碼
public class Solution_56 { /** * 給一個連結串列,若其中包含環,請找出該連結串列的環的入口結點,否則,輸出null。 */ //方法一 public ListNode EntryNodeOfLoop_1(ListNode pHead) { //最後一個結點指向的下一個結點就是入口結點 //list 的contains方法時間複雜度為n^2,儘量用set的add方法,時間複雜度為O(1) if(pHead == null || pHead.next == null){ return null; } List<ListNode> list = new ArrayList<ListNode>(); while(!list.contains(pHead)){ list.add(pHead); pHead = pHead.next; } return pHead; } //方法二、快慢指標思路 時間複雜度為O(n),空間複雜度為O(1) public ListNode EntryNodeOfLoop(ListNode pHead){ if(pHead == null || pHead.next == null){ //list 的contains方法時間複雜度為n^2,儘量用set的add方法,時間複雜度為O(1) return null; } ListNode fast = pHead.next.next; ListNode slow = pHead.next; //先求出相遇點,同時也判斷了是否有沒有環 while(fast!=slow){ if(fast.next.next!=null && slow.next!=null){ //是個環 fast = fast.next.next; slow = slow.next; }else { return null; //不是環 } } //找出相遇點後再用塊指標指向phead,並且和slow每次都向後移動一個結點 fast = pHead; while(fast!=slow){ fast = fast.next; slow = slow.next; } return fast; } }