链表判断是否有环:可以采用快指针与慢指针的方式来解决。即定义一个快指针fast和一个慢指针slow,使得fast每次跳跃两个节点,slow每次跳跃一个节点。如果链表没有环的话,则slow与fast永远不会相遇(这里链表至少有两个节点);如果有环,则fast与slow将会在环中相遇。判断出链表有环以后,则需要算出进入环的第一个节点。在fast与slow第一次相遇后,设置一个节点pNode从链表的头部开始遍历,每次只遍历一个节点。这样,当fast与pNode再次相遇时,pNode所处的位置便是环的首部。
解题关键:因为有环,所以快慢指针在第一次相遇和第二次相遇以及以后的每一次相遇的状态都是一样的,所以当第一次相遇时找到相遇点后,此时从头设置一个新的节点标志,每次只遍历一个节点,当再次相遇时,此节点所处的位置即是环的头部。
我们使用两个指针,fast 与 slow。它们起始都位于链表的头部。随后,slow 指针每次向后移动一个位置,而 fast 指针向后移动两个位置。如果链表中存在环,则 fast 指针最终将再次与 slow 指针在环中相遇。
如下图所示,设链表中环外部分的长度为 a。slow 指针进入环后,又走了 b 的距离与 fast 相遇。此时,fast 指针已经走完了环的 n 圈,因此它走过的总距离为a+n(b+c)+b=a+(n+1)b+nc。
根据题意,任意时刻,fast 指针走过的距离都为 slow 指针的 2 倍。因此,我们有
a+(n+1)b+nc=2(a+b)⟹a=c+(n−1)(b+c)
有了 a=c+(n-1)(b+c) 的等量关系,我们会发现:从相遇点到入环点的距离加上 n−1 圈的环长,恰好等于从链表头部到入环点的距离。
因此,当发现 slow 与 fast 相遇时,我们再额外使用一个指针 ptr。起始,它指向链表头部;随后,它和 slow 每次向后移动一个位置。最终,它们会在入环点相遇。
code:
public class Solution {
public ListNode detectCycle(ListNode head) {
ListNode fast = head;
ListNode slow = head;
while(fast!=null && fast.next!=null){
slow = slow.next;
fast = fast.next.next;
if(slow == fast){
ListNode pNode = head;
while(slow != pNode){
slow = slow.next;
pNode = pNode.next;
}
return pNode;
}
}
return null;
}
}
LNode* GetLoopNode(LNode* head)
{
//前置条件的判断
if (!head)
{
return NULL;
}
//定义一个快指针和一个慢指针
LNode* fast = head;
LNode* slow = head;
while (fast && (fast->next))
{
fast = fast->next->next;
slow = slow->next;
if (fast == slow)
{
//如果有环,则返回环的第一个节点
slow = head;
while (1)
{
fast = fast->next;
slow = slow->next;
if (fast == slow)
{
break;
}
}
return slow;
}
}
return NULL;
}