Question:
Given a linked list, return the node where the cycle begins. If there is no cycle, return null
.
Note: Do not modify the linked list.
Tips:
給定一個連結串列,如果連結串列中有環,返回環開始的結點。如果連結串列無環 返回null。
思路:
之前141題(連結),判斷連結串列中是否有環,返回布林型別的變數。本題還需要返回環開始的結點。解決過程分為以下幾步。
(1)設定兩個速度不同的指標,fast與slow。fast的速度是slow的二倍。
fast = fast.next.next;
slow = slow.next;
當兩個指標相遇,證明連結串列存在結點,否則,fast指標先遇到null證明連結串列內無環。
(2)在兩個指標相遇處切開,則變成了求兩個單連結串列第一個公共結點的問題160(連結)。
slow路程:S1=L+a;(L連結串列無環部分長度,a為slow指標走過的環內的弧長)
fast路程:S2=L+a+n*k;(n表示環內結點長度,k表示 fast指標走過的環的圈數)
路程=速度*時間。slow的速度*2=fast的速度。相遇代表時間相等。則S1*2=S2。
2(L+a)=L+a+n*k; ==> L+a=n*k
==> L=n*k-a;
從上面的分析可以看出,連結串列中,不在環內的長度L=環的長度n - slow走過的弧長。
這時,新建一個指標,從head開始向後移動,相遇處的指標繼續後移,當兩個指標再次相遇,則該節點是環開始的第一個結點。
程式碼:
public ListNode detectCycle(ListNode head) { ListNode fast = head; ListNode slow = head; ListNode meet = null; while (fast != null) { if (fast.next != null) { fast = fast.next; if (fast.next != null) { fast = fast.next; slow = slow.next; if (fast == slow) { //fast與slow相遇 meet = slow; break; } } else return null; } else return null; } ListNode haha = head; //迴圈結束,兩個指標均指向環開始的結點。 while (haha != meet) { haha = haha.next; meet = meet.next; } return meet; }
測試程式碼:
public static void main(String[] args) { ListNode head1 = new ListNode(1); ListNode head2 = new ListNode(2); ListNode head3 = new ListNode(3); ListNode head4 = new ListNode(4); ListNode head5 = new ListNode(5); ListNode head6 = new ListNode(6); head1.next = head2; head2.next = head3; head3.next = head4; head4.next = head5; head5.next = head6; head6.next = head3; L142LinkedListCycleII l142 = new L142LinkedListCycleII(); ListNode head = l142.detectCycle(head1); if(head!=null){ System.out.println(head.val); }else{ System.out.println("Null"); } }