题目
给定一个链表,返回链表开始入环的第一个节点。 如果链表无环,则返回 null。
为了表示给定链表中的环,使用整数 pos 来表示链表尾连接到链表中的位置(索引从 0 开始)。 如果 pos 是 -1,则在该链表中没有环。
思路
考查知识点:
●判断链表是否有环(链表操作)
●如果有环,如何找到这个环的入口(数学运算)
1.判断链表是否有环
使用快慢指针法,fast一次走两步,slow一次走一步,二者相遇,说明有环,因为fast一定先进入环中,相遇必定是环中相遇,而且有环一定会相遇(画图理解)
2.如果有环,如何找到这个环的入口
先说结论:在快慢指针相遇处设一个指针index1,在头结点处设一个指针index2,二者一次走一步,二者相遇的地方就是环的入口
数学推导:
假设从头结点到环形入口节点的节点数为x。 环形入口节点到 fast指针与slow指针相遇节点 节点数为y。 从相遇节点 再到环形入口节点节点数为 z。
那么相遇时: slow
指针走过的节点数为: x + y
, fast
指针走过的节点数:x + y + n (y + z)
,n
为fast
指针在环内走了n
圈才遇到slow
指针, (y+z)
为 一圈内节点的个数。
因为fast
指针是一步走两个节点,slow
指针一步走一个节点, 所以 fast
指针走过的节点数 = slow
指针走过的节点数 * 2:
(x + y) * 2 = x + y + n (y + z)
两边消掉一个(x+y):
x + y = n (y + z)
因为要找环形的入口,那么要求的是x,因为x表示头结点到环形入口节点的的距离。
所以要求x ,将x单独放在左面:
x = n (y + z) - y
再从n(y+z)中提出一个 (y+z)来,整理公式之后为如下公式:
x = (n - 1) (y + z) + z
注意这里n一定是大于等于1的,因为 fast指针至少要多走一圈才能相遇slow指针。
当n = 1 时,公式就化解为
x = z
这就意味着,从头结点出发一个指针,从相遇节点也出发一个指针,这两个指针每次只走一个节点, 那么当这两个指针相遇的时候就是 环形入口的节点。
也就是在相遇节点处,定义一个指针index1
,在头结点处定一个指针index2
。
让index1
和index2
同时移动,每次移动一个节点, 那么他们相遇的地方就是 环形入口的节点。
同理当n > 1
的时候,就是fast
指针在环内转n
圈之后才找到slow
指针,和n = 1
的情况是一样的,只不过index1
指针在环内多转了n-1
圈,才找到index2
指针,相遇点仍然是环的入口处
java代码
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(fast == slow){//有环
ListNode index1 = fast;
ListNode index2 = head;
// 两个指针,从头结点和相遇结点,各走一步,直到相遇,相遇点即为环入口
while(index1 != index2){
index1 = index1.next;
index2 = index2.next;
}
return index1;
}
}
return null;
}
}