劍指Offer題目:鏈表中環的入口結點
題目描述
給一個鏈表,若其中包含環,請找出該鏈表的環的入口結點,否則,輸出null。
解題思路
有兩種做法
做法一
利用快慢指針的進度關係找到規律:
假設環長度爲n
,進入環之前結點個數爲x
;
slow在環內走到第k
個結點,繞環走了s
圈;
fast繞環走了f
圈,則有2(x+sn+k)=x+fn+k
可以得出x =(f-2sn) - k
。此時slow距入口結點還剩 n-k
個結點,x=(f-2sn−1)n+n−k
,即一個指針從鏈表頭節點走到環入口的長度等於另一個指針從相遇的位置走 f-2sn−1
圈後再走n-k
的長度,也就是說兩個指針相遇後,讓一個指針回到頭節點,另一個指針不動,然後他們同時往前每次走一步,當他們相遇時,相遇的節點即爲環入口節點。
所以,我們設置兩個指針,一個是快指針fast,一個是慢指針slow,fast一次走兩步,slow一次走一步,如果單鏈表有環那麼當兩個指針相遇時一定在環內。此時將一個指針指到鏈表頭部,另一個不變,二者同時每次向前移一格,當兩個指針再次相遇時即爲環的入口節點。如果fast走到null則無環。
做法二
還是首先使用快慢指針去判斷鏈表是否成環,如果成環後,使用斷鏈法從鏈表頭部開始遍歷,每走到一個結點,就將其與上一個遍歷到的結點斷開。這樣,最後訪問到的非空結點就是環的入口結點。
完整代碼
public class q56_鏈表中環的入口結點_EntryNodeOfLoop {
/**
* 思路1:利用快慢指針的進度關係找到規律
*
* @param pHead
* @return
*/
public ListNode EntryNodeOfLoop1(ListNode pHead) {
if (pHead == null || pHead.next == null) return null;
ListNode slow = pHead;
ListNode fast = pHead;
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;
ListNode newStart = pHead;
while (slow != null && newStart != null) {
if (slow == newStart) return slow;
slow = slow.next;
newStart = newStart.next;
}
return null;
}
/**
* 思路2:斷鏈法
* @param pHead
* @return
*/
public ListNode EntryNodeOfLoop2(ListNode pHead) {
if (pHead == null || pHead.next == null) return null;
ListNode slow = pHead;
ListNode fast = pHead;
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;
ListNode node = pHead;
ListNode pre = null;
//開始斷鏈
while (node != null) {
pre = node;
node = node.next;
pre.next = null;
}
return pre;
}
public static void main(String[] args) {
ListNode root=new ListNode(1);
ListNode node=root;
for (int i = 2; i <5 ; i++) {
node.next=new ListNode(i);
node=node.next;
}
ListNode ans=new q56_鏈表中環的入口結點_EntryNodeOfLoop().EntryNodeOfLoop2(root);
}
}
更多LeetCode題目及答案解析見GitHub: https://github.com/on-the-roads/LeetCode
劍指offer題目及答案解析:https://github.com/on-the-roads/SwordToOffer