Linked List Cycle II
Given a linked list, return the node where the cycle begins.
If there is no cycle, return null.
- 首先找到meetingnode
(快慢指针在圈里面循环最后一定会相交,并且是在慢指针的第一圈)- 制造二次相遇:重新定义两个指针,一个指针指向head, 一个指针指向快慢指向的相遇点,然后这两个指针不断遍历(同时走一步),当它们指向同一个结点时即是环的入口结点
假设上图中的 7 为快慢指针相遇的结点,不难看出慢指针走了 L + S 步****(一定会在第一圈遇到),快指针走得比慢指针更快,它除了走了 L + S 步外,还额外在环里绕了 n 圈,所以快指针经历了 L+S+nR 步(R为图中环的长度),另外我们知道每遍历一次,慢指针走了一步,快指针走了两步,所以快指针走的路程是慢指针的两倍,即 2 (L+S) =L+S+nR,即 L+S = nR(记住n=1的示例就好)
- 当 n = 1 时,则 L+S = R 时,则从相遇点 7 开始遍历走到环入口点 2 的距离为 R – S = L,刚好是环的入口结点,而 head 与环入口点 2 的距离恰好也为 L,所以只要在头结点定义一个指针,在相遇点(7)定义另外一个指针,两个指针同时遍历,每次走一步,必然在环的入口位置 2 相遇
- 当 n > 1时,L + S = nR,即 L = nR – S, nR-S 怎么理解?可以看作是指针从结点 7 出发,走了 n 圈后,回退 S 步,此时刚好指向环入口位置,也就是说如果设置一个指针指向 head(定义为p1), 另设一个指针指向 7(定义为p2),不断遍历,p2 走了nR-S 时(即环的入口位置),p1也刚好走到这里(此时 p1 走了 nR-S = L步,刚好是环入口位置),即两者相遇!
/**
* Definition for singly-linked list.
* class ListNode {
* int val;
* ListNode next;
* ListNode(int x) {
* val = x;
* next = null;
* }
* }
*/
public class Solution {
public ListNode detectCycle(ListNode head) {
if(head==null||head.next==null){
return null;
}
ListNode meetingnode= iscycle(head);
if (meetingnode==null){
return null;
}
//制造二次相遇
ListNode fast=head;
ListNode slow=meetingnode;
while(fast!=slow){
fast=fast.next;
slow=slow.next;
}
return slow;
}
//找meetingnode
ListNode iscycle(ListNode head){
if(head==null||head.next==null){
return null;
}
ListNode fast=head;
ListNode slow=head;
while(fast!=null&&fast.next!=null){//环和非环的条件
fast=fast.next.next;
slow=slow.next;
if(fast==slow){
return slow;
}
}
return null;
}
}
- 快慢指针相遇时,慢指针一定在第一圈,而快指针可能走了n圈
【证明1】假设当慢指针进入环时,慢指针在快指针前 m 处(顺指针方向)(0<m<环长,如果 m=0 or m=环长 ,说明此时快慢指针正好在环入口处相遇),假设这之后快慢指针又移动了 x 次后相遇,此时慢指针移动的距离即为 x, 快指针移动的距离2x,两者相遇,则 x+m = 2x ⟹ x = m 。而m小于环长,所以慢指针一定在第一圈时就和快指针相遇。
一个人是跳1个格子,另一个跳2个格子,会不会每次要相遇的时候,快的都会跳过慢的那个,从而不会相遇在一个格子。(重点是为什么一定会相遇在一个格子)
证明:通俗点可以理解为他们的相对速度只差一个格子,快的只能一个一个格子的去追慢的,必然在一个格子相遇!如果没看懂,看下面的详细。一次跳2个与一次跳一个格子的追上之后,是一定会在一个格子遇到的。因为在即将追上的时候,快的那个落后慢的1个或者2个格子,无论哪种,落后1个的话,下一步正好追上,落后2个格子的话,下一步就落后1个格子了,也可以说即将追上的时候一定是相差1个格子,下一步一定在一个格子相遇。