本文參考自《劍指offer》一書,代碼采用Java語言。
題目
一個鏈表中包含環,如何找出環的入口結點?例如,在圖3.8的鏈表中,環的入口結點是結點3。
思路
1.確定鏈表是否有環:通過兩個不同速度的指針確定,當兩個指針指向同一個結點時,該結點為環中的一個結點。
2.確定環中結點的數目n:指針走一圈,邊走邊計數
3.找到環的入口:從頭結點開始,通過兩個相差為n的指針來得到(即尋找鏈表中倒數第n個結點)
更簡單的思路:【LeetCode】142. Linked List Cycle II
測試算例
1.功能測試(鏈表包含與不包含環;鏈表有多個或一個結點)
2.特殊測試(頭結點為null)
Java代碼
package _23;
/**
*
* @Description 鏈表中環的入口結點
*
* @author yongh
* @date 2018年10月15日 下午2:35:14
*/
//題目:一個鏈表中包含環,如何找出環的入口結點?例如,在圖3.8的鏈表中,
//環的入口結點是結點3。
/*
* 思路:1.確定鏈表是否有環:通過兩個不同速度的指針確定
* 2.確定環中結點的數目n:指針走一圈,邊走邊計數
* 3.找到環的入口:從頭結點開始,通過兩個相差為n的指針來得到(即尋找鏈表中倒數第n個結點)
*/
public class EntryNodeInListLoop {
public class ListNode {
int val;
ListNode next = null;
ListNode(int val) {
this.val = val;
}
}
/*
* 確定鏈表是否有環,采用快慢指針確定
* 返回值代表快慢指針相遇時的結點,返回null代表鏈表無環
*/
private ListNode meetingNode(ListNode head) {
if(head==null)
return null;
ListNode pSlow=head;
ListNode pFast=head;
while(pFast!=null) {
pSlow=pSlow.next;
pFast=pFast.next;
if(pFast!=null)
pFast=pFast.next;
if(pSlow!=null && pSlow==pFast)
return pSlow;
}
return null;
}
/**
* 計算環中入口結點
*/
public ListNode entryNodeOfLoop(ListNode head) {
ListNode meetingNode=meetingNode(head);
if(meetingNode==null)
return null;
//計算環中結點的數目
int count=1; //環中結點的數目
ListNode pNode1 = meetingNode.next;
while(pNode1!=meetingNode){
count++;
pNode1=pNode1.next;
}
//先移動pNode1,次數為count
pNode1=head;
for(int i=1;i<=count;i++) {
pNode1=pNode1.next;
}
ListNode pNode2=head;
while(pNode1!=pNode2) {
pNode1=pNode1.next;
pNode2=pNode2.next;
}
return pNode1;
}
}
收獲
1.通過兩個不同速度的指針可以確定鏈表中是否有環
2.相差n步的兩個指針可以找到倒數第n個結點(鏈表中倒數第k個結點)
3.復雜問題分解成為幾個簡單問題(本題分為三步:找出環中任一結點;得到環的個數;找到入口結點)
