目录
环形链表II
描述
给定一个链表,返回链表开始入环的第一个节点。 如果链表无环,则返回 null。
为了表示给定链表中的环,我们使用整数 pos 来表示链表尾连接到链表中的位置(索引从 0 开始)。 如果 pos 是 -1,则在该链表中没有环。注意,pos 仅仅是用于标识环的情况,并不会作为参数传递到函数中。
说明:不允许修改给定的链表。
进阶:
你是否可以使用 O(1) 空间解决此题?
示例 1
输入:head = [3,2,0,-4], pos = 1 输出:返回索引为 1 的链表节点 解释:链表中有一个环,其尾部连接到第二个节点。
示例 2
输入:head = [1,2], pos = 0 输出:返回索引为 0 的链表节点 解释:链表中有一个环,其尾部连接到第一个节点。
示例 3
输入:head = [1], pos = -1 输出:返回 null 解释:链表中没有环。
提示
- 链表中节点的数目范围在范围 内
- pos 的值为 -1 或者链表中的一个有效索引
数据结构
public class ListNode {
int val;
ListNode next;
ListNode() {}
ListNode(int val) { this.val = val; next=null; }
}
方法:双指针
我们设定两个指针fast、slow同时指向链表头节点head,fast指针一次走两步,slow指针一次走一步,有以下两种情况:
- 链表中不存在环:此时fast应该会先访问到末尾的空节点,返回null即可
- 链表中存在环:那么fast和slow就会相遇,因为二者会在环里面不停追赶,由于二者步长不同,所以最终一定会在环内某个位置相遇
我们来讨论一下链表中存在环的情况,假设链表长度为a+b(链表头节点到环入口有a个节点,环内有b个节点),假设二者相遇时fast指针走了f步,slow指针走了s步,那么f和s满足以下关系:
- f=2s;因为快指针一次走2步,慢指针一次走1步,所以相同时间内f=2s
- f=s+nb;因为二者在环内相遇,肯定是快指针追上了慢指针(即跑步运动所说的套圈了),所以快指针比慢指针要多走了n个环的长度,即f=s+nb
由上面两个等式我们可以求解出:
- f=2nb
- s=nb
此时我们假设让一个指针走k步走到链表环入口节点处,那么k满足k=a+nb。(走a步是第一次到达环的入口,后面每走b步都会重新到达入口节点处)
所以当快慢指针第一次相遇时,慢指针只需要再走a步就可以到达环的入口节点了,但是我们不知道a的大小,我们的解决办法是让此时快指针从链表头节点开始走,每轮走1步,当二者相遇时就是在环入口处,并且二者都走了a步。
public class Solution {
public ListNode detectCycle(ListNode head) {
ListNode fast=head,slow=head;//快慢节点同时从头节点触发
do{
if (fast==null||fast.next==null) return null;
fast=fast.next.next;//快指针每轮走两步
slow=slow.next;
}while (fast!=slow);
//第一轮相遇后,此时慢指针还差a步到达环入口处
//将快指针放置在头节点,每轮走1步,第二次相遇时即为环入口
fast=head;
while (fast!=slow){
fast=fast.next;
slow=slow.next;
}
return fast;//返回结果
}
}