本文參考自《劍指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.復雜問題分解成為幾個簡單問題(本題分為三步:找出環中任一結點;得到環的個數;找到入口結點)