剑指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