鏈表中環的起點 Linked List Cycle II

問題:html

Given a linked list, return the node where the cycle begins. If there is no cycle, return null.node

Note: Do not modify the linked list.算法

Follow up:
Can you solve it without using extra space?spa

解決:.net

① 使用快慢指針,記錄兩個指針相遇的位置,當兩個指針相遇了後,讓其一指針從鏈表頭開始向後走,另外一個從相遇的點開始,此時再相遇的位置就是鏈表中環的起始位置指針

/**
 * Definition for singly-linked list.
 * class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode(int x) {
 *         val = x;
 *         next = null;
 *     }
 * }
 */
public class Solution { //2ms
    public ListNode detectCycle(ListNode head) {
        if (head == null || head.next == null) {
            return null;
        }
        ListNode slow = head;
        ListNode fast = head;
        while(fast != null && fast.next != null){
            slow = slow.next;
            fast = fast.next.next;
            if (slow == fast) {//有環
                break;
            }
        }
        if (fast == null || fast.next == null) {//不存在環
            return null;
        }
        slow = head;
        while(slow != fast){
            slow = slow.next;
            fast = fast.next;
        }
        return fast;
    }
}code

② 進化版htm

public class Solution { //1ms
    public ListNode detectCycle(ListNode head) {
        ListNode slow = head;
        ListNode fast = head;
        while (slow != null && fast != null && fast.next != null) {
            slow = slow.next;
            fast = fast.next.next;
            if (slow == fast && slow != null) {
                ListNode slow2 = head;
                while (slow != slow2) {
                    slow = slow.next;
                    slow2 = slow2.next;
                }
                return slow;
            }
        }
        return null;
    }
}blog

【總結】鏈表找環方法證實 http://www.cnblogs.com/wuyuegb2312/p/3183214.html同步

一: 判斷兩個鏈表是否相交,假設兩個鏈表不帶環。https://my.oschina.net/liyurong/blog/994540

解決方法:用指針p一、p2分別指向兩個鏈表頭,不斷後移;最後到達各自表尾時,若p1==p2,那麼兩個鏈表必相交。

二: 若是鏈表可能有環,上面的方法怎麼調整?

分狀況討論:
若是兩個鏈表都沒有環,那麼同原算法;
若是兩個鏈表一個有環,一個沒環,那麼必然不相交。
(*)若是兩個鏈表都有環,判斷一個鏈表環上的任一點是否在另外一個鏈表上,若是是,則必相交,反之不相交。這時,須要找到另外一個鏈表完整的環都包括了哪些結點,才能進行判斷。(*)
能夠看出,解答這個問題重點在於判斷是否有環

三: 若是必需要求出兩個鏈表相交的第一個節點呢?

(*)    思路:若是兩個尾結點是同樣的,說明它們有重合;不然兩個鏈表沒有公共的結點。
    在上面的思路中,順序遍歷兩個鏈表到尾結點的時候,咱們不能保證在兩個鏈表上同時到達尾結點。這是由於兩個鏈表不必定長度同樣。但若是假設一個鏈表比另外一個長L個結點,咱們先在長的鏈表上遍歷L個結點,以後再同步遍歷,這個時候咱們就能保證同時到達最後一個結點了。因爲兩個鏈表從第一個公共結點開始到鏈表的尾結點,這一部分是重合的。所以,它們確定也是同時到達第一公共結點的。因而在遍歷中,第一個相同的結點就是第一個公共的結點。
    在這個思路中,咱們先要分別遍歷兩個鏈表獲得它們的長度,並求出兩個長度之差。在長的鏈表上先遍歷若干次以後,再同步遍歷兩個鏈表,直到找到相同的結點,或者一直到鏈表結束。PS:沒有處理一種特殊狀況:就是一個是循環鏈表,而另外一個也是,只是頭結點所在位置不同。 (*)
    對於特殊狀況,相交的第一個結點能夠是第一個鏈表的表頭,也能夠是第二個鏈表的表頭。由於從表頭開始兩者就開始相交了。對於這種狀況,若是判斷出兩者都是循環鏈表,就能夠直接返回其中之一的頭指針。

四: 求鏈表倒數第k個結點

(*)設置兩個指針p1,p2,首先p1和p2都指向head,而後p2向前走k步,這樣p1和p2之間就間隔k個節點,最後p1和p2同時向前移動,直至p2走到鏈表末尾。(*)

【總結】如今來看,遺留問題是:

1.如何判斷鏈表有環?

2.如何找到鏈表環的入口?

簡單來講就是:

1. 對於問題1,指針p一、p2指向表頭,每次循環p1指向後繼,p2指向後繼的後繼;循環的結束條件是,p2後繼爲空(無環)或p1==p2(有環)

下面用易於理解的方式證實,這個解法中若是有環,p1和p2必同時在停留在某個節點。

如左圖,在任意時刻,p1和p2都在環上。因爲p1每次向前1步,p2每次向前兩步,用相對運動的觀點來看,把p1看做靜止,那麼p2每次相對p1向前1步,兩者在順時針方向上的距離每通過一個時刻就減小1,直到變爲0,也即兩者剛好相遇。這樣就證實了在離散狀況下,對於有環鏈表,兩者也是必然在某一時刻相遇在某個節點上的。

2. 對於問題2尋找環的入口問題。

設:鏈表頭是X,環的第一個節點是Y,slow和fast第一次的交點是Z。各段的長度分別是a,b,c,如圖所示。環的長度是L。slow和fast的速度分別是qs,qf。

第一次相遇時slow走過的距離:a+b,fast走過的距離:a+b+c+b

由於fast的速度是slow的兩倍,因此fast走的距離是slow的兩倍,有 2(a+b) = a+b+c+b,能夠獲得a=c(這個結論很重要!)

咱們發現L=b+c=a+b,也就是說,從一開始到兩者第一次相遇,循環的次數就等於環的長度。

咱們已經獲得告終論a=c,那麼讓兩個指針分別從X和Z開始走,每次走一步,那麼正好會在Y相遇!也就是環的第一個節點。

在上一個問題的最後,將c段中Y點以前的那個節點與Y的連接切斷便可。

如何判斷兩個單鏈表是否有交點?先判斷兩個鏈表是否有環,若是一個有環一個沒環,確定不相交;若是兩個都沒有環,判斷兩個列表的尾部是否相等;若是兩個都有環,判斷一個鏈表上的Z點是否在另外一個鏈表上。

如何找到第一個相交的節點?求出兩個鏈表的長度L1,L2(若是有環,則將Y點當作尾節點來算),假設L1<L2,用兩個指針分別從兩個鏈表的頭部開始走,長度爲L2的鏈表先走(L2-L1)步,而後兩個一塊兒走,直到兩者相遇。

 

【總結】尋找環存在和環入口的方法:

用兩個指針p一、p2指向表頭,每次循環時p1指向它的後繼,p2指向它後繼的後繼。若p2的後繼爲NULL,代表鏈表沒有環;不然有環且p1==p2時循環能夠終止。此時爲了尋找環的入口,將p1從新指向表頭且仍然每次循環都指向後繼,p2每次也指向後繼。當p1與p2再次相等時,相等點就是環的入口。

相關文章
相關標籤/搜索