題目描述:
給一個鏈表,若其中包含環,請找出該鏈表的環的入口結點,否則,輸出null。
解題思路:
本題是一個比較典型的鏈表題目,難度適中。首先,對於大多人來說,看到這道題是比較開心的,因為判斷一個鏈表是否存在環的方法,基本上大家都知道,就是快慢指針法,但是再仔細一看,本題除了判斷是否有環之外,還要找到這個環的入口點,這就略有些復雜了。
具體思路如下:
第一步:確定一個鏈表是否有環。這一步就是快慢指針法,定義兩個指針,同時從鏈表的頭結點出發,快指針一次走兩步,慢指針一次走一步。如若有環,兩個指針必定相遇,也就是如果快指針反追上了慢指針,說明存在環(這里要注意,兩指針相遇的地方一定在環中,但不一定是環的入口),如果快指針走到了鏈表的末尾(指向了NULL),則說明不存在環。
第二步:找到環的入口點。這還是可以利用雙指針來解決,兩個指針初始都指向頭結點,如果我們可以知道環中的結點個數,假設為n,那么第一個指針先向前走n步,然后兩個指針(另一個從頭結點開始)同時向前,當兩個指針再次相遇時,他們的相遇點正好就是環的入口點。
這其實並不難理解,假設鏈表中共有m個結點,環中有n個結點,那么除環以外的結點數就是m-n,第一個指針先走了n步,然后兩個指針一起向前,當他們一起向前m-n步時,第一個鏈表正好走完一遍鏈表,返回到環的入口,而另一個指針走了m-n步,也正好是到了環的入口。
現在,我們還有一個關鍵的問題:如何知道鏈表中的環包含了幾個結點呢?也就是,怎么求這個n。
實際上這也不難,在第一步中,我們提到:快慢指針相遇的地方一定在環中,並且通過第一步我們已經找到了這個位置,接下來,只要從這個相遇的結點出發,一邊移動一邊計數,當它繞着環走一圈,再次回到這個結點時,就可以得到環中的結點數目n了。
舉例:

編程實現(Java):
public class ListNode {
int val;
ListNode next = null;
ListNode(int val) {
this.val = val;
}
}
public class Solution {
public ListNode EntryNodeOfLoop(ListNode pHead){
if(pHead==null||pHead.next==null)
return null;
ListNode meet=meetingNode(pHead); //相遇的節點
if(meet==null)
return null;
//求環的長度
ListNode temp=meet;
int len=1;
temp=temp.next;
while(temp!=meet){
temp=temp.next;
len++;
}
ListNode fast=pHead,slow=pHead;
//快指針先走len步
for(int i=0;i<len;i++)
fast=fast.next;
while(slow!=fast){
slow=slow.next;
fast=fast.next;
}
return slow;
}
//判斷是否有環,返回相遇的節點,思路:快慢指針,若有環必相遇
public ListNode meetingNode(ListNode pHead){
if(pHead==null||pHead.next==null)
return null;
ListNode fast=pHead,slow=pHead;
while(fast.next!=null){
slow=slow.next;
fast=fast.next.next;
if(slow==fast)
return slow;
}
return null;
}
}
通過查看相關文章發現,實際上,當找到在環中相遇的結點meetingNode時,一個指針指向meetingNode,另一個指針指向head,然后一起向前,這兩個指針相遇的位置就一定是環的入口點,這個證明比較復雜,但實際上這和先走n步的解法本質上還是一樣的,只不過是在環里面多繞幾圈。可以參考一下 https://zhuanlan.zhihu.com/p/85349101 的證明過程。
public class Solution {
public ListNode EntryNodeOfLoop(ListNode pHead) {
//鏈表中環的入口結點
if(pHead==null)
return null;
ListNode meetingNode=hasCycle(pHead);
if(meetingNode==null) //不存在環
return null;
ListNode head=pHead;
while(head!=meetingNode){
head=head.next;
meetingNode=meetingNode.next;
}
return head;
}
public ListNode hasCycle(ListNode pHead){
//判斷是否有環,快慢指針法
if(pHead==null || pHead.next==null)
return null;
ListNode slow=pHead,fast=pHead;
while(fast!=null && fast.next!=null){
slow=slow.next;
fast=fast.next.next;
if(slow==fast)
return slow;
}
return null;
}
}