1. 程式人生 > >連結串列中環的入口結點(劍指offer56題)

連結串列中環的入口結點(劍指offer56題)

一、題目描述

給一個連結串列,若其中包含環,請找出該連結串列的環的入口結點,否則,輸出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;
    }
}