問題: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再次相等時,相等點就是環的入口。