目录
题目描述:
给定一个链表的头节点head,返回链表开始入环的第一个节点。如果链表没有环,则返回null。
如果链表中有某个节点,可以通过连续跟踪 next 指针再次到达,则链表中存在环。 为了表示给定链表中的环,评测系统内部使用整数 pos 来表示链表尾连接到链表中的位置(索引从 0 开始)。如果 pos 是 -1,则在该链表中没有环。注意:pos 不作为参数进行传递,仅仅是为了标识链表的实际情况。不允许修改链表。
题目链接:142. 环形链表 II - 力扣(LeetCode)
这个题主要考察了两个点:
1、判断链表是否有环
2、如果有环,怎么才能找到这个环的入口
1、判断链表是否有环
快慢指针法
分别定义fast和slow指针,同时从头结点出发,fast每次走两个结点,slow每次走一个结点,如果两个指针相遇,则说明这个链表有环。
解释:可以自己试着画下图,无论怎么走都会出现fast是slow前一个结点这种情况,这两个指针再各自走一步就会相遇。
知道思路后,也可以尝试写下面这道题
2、找环的入口
通过第一步操作,我们已经知道了这个链表是否有环,如果有,那么该如何找到环的入口呢?
我们可以将本题抽象为一个数学题,假设a点为两指针相遇点,b点为该环的入口,也是本题中要求的结点,c点为头结点,根据下图的解释,得出了一个数学公式:x=(k-1)*(m+n)+n
(m+n)就是环的周长,当k=1的时候,说明fast指针在环里转了一圈然后与slow指针相遇,此时上述的数学公式就可以化简为 x=n 。也就是说,一个指针从a点出发,另一个指针从c点出发,两个指针每次都只走一个结点,他两相遇的那个结点就是我们所要求的入口结点。
总结:用快慢指针法判断是否有环,如果有环,得到相遇结点a,再设置两个指针ans和cur同时从a点和c点出发,那么他们相遇的结点b点就是环形入口的结点。
代码如下:
public class Solution {
//找a点
public ListNode hasCycle(ListNode head) {
ListNode fast=head;
ListNode slow=head;
//fast走的快,fast为null则说明,该链表没有环
while(fast!=null&&fast.next!=null){
fast=fast.next.next;
slow=slow.next;
//相等即有环,返回相遇点a
if(fast==slow){
return slow;
}
}
return null;
}
//找b点
public ListNode detectCycle(ListNode head) {
ListNode ans=hasCycle(head); //记录相遇点
ListNode cur=head;
//没有环返回null
if(ans==null){
return null;
}
while(cur!=ans){
cur=cur.next;
ans=ans.next;
}
return cur;
}
}