给定一个链表,返回链表开始入环的第一个节点。 如果链表无环,则返回 null。
说明:不允许修改给定的链表。
你是否可以不用额外空间解决此题?
https://leetcode-cn.com/problems/linked-list-cycle-ii/description/
/**
* 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) {
return null;
}
ListNode fast = head;
ListNode slow = head;
while(fast!=null && fast.next!=null){
fast = fast.next.next;
slow = slow.next;
if(fast==slow) {
slow = head;
while(fast != slow ){
fast = fast.next;
slow = slow.next;
}
return slow;
}
}
return null;
}
}
说明:
- 定义两个指针,快指针每次走两步,慢指针一次走一步,当环存在时,则快指针一定会追上慢指针;
- 追上时使其中一个指针指向链头,然后两个指针同时走,则再次追上时指针的位置为环开始的位置
???
为什么快指针一定会追上慢指针?
快指针F 比慢指针S 先进环,假设进环时F离S还差a, S 走b步后F追上,则
2b (F走的距离) = b + a (S走的距离+原来的差距)
即b=a,当S进环时,F离S多远则再过多少步可以追上。
想起小学奥数,甲和乙在夕阳下的奔跑。(追逐问题)
为什么快指针在慢指针走完环一圈之内追上?
有环的情况下,在S入环时,F肯定也在环内,此时有两种情况
- 假设F也在同时在入环的第一个节点(环入口为在链头时,同时入环;或者F绕了N圈的环回到环入口),则当第一次追上时,F追了S一圈。
- 假设F不在环入口,则此时F需要追的距离就小于一圈了
综上:追的过程中,S走的距离<=一圈
为什么第二次追上后的点为入环节点?
假设链表为
1->2->3->...->m->...->n->m
//链表长度为n,m为入环点。则有1<=m<=n
第一次追上时花费时间为x,F多走了y圈
//慢指针所在的位置 = 快指针所在位置 (快指针走的距离-环的长度 * y)
1+x = 1+2x-(n-m+1)y
得出:
x = (n-m+1)y; //环的长度*y
第二次,S从头开始,F从当前追上位置开始,同时以一步的速度走。则,S走到环入口m位置需要m-1
步; 而F走了m-1
步后位置为:
1+2x-(n-m+1)y + m-1
=> m + (n-m+1)y //这个位置表示到入口m后绕了y圈的环,仍然在环入口m
第二次追上的位置为环入口得证。