题目
判断一个链表是否有环,如果有,则返回第一个进入环的节点,没有则返回null。
思路
如果一个链表没有环,那么遍历链表一定可以遇到链表的终点;如果链表有环,那么遍历链表就永远在环里转下去了。如何找到第一个入环节点,具体过程如下:
- 设置一个慢指针slow和一个快指针fast。slow每次走一步,fast每次走两步,遍历链表。
- 如果链表无环,fast一定会先到终点,并返回null。
- 如果有环,fast指针和slow指针会在环中某个位置相遇;此时,fast指针重新回到head位置,slow指针不动。接下来,fast指针每次从移动两步变成移动一步,slow指针依然每次移动一步,然后继续遍历。
- fast指针和slow指针一定会在入环的节点处相遇。
证明:
- 假设该链表总长度为N个节点,从头节点到入口节点有M个节点,环长L个节点
- 假设第一次相遇时,slow走了k个节点,2k-k=x*L,解释一下,fast走了2k个节点,他们要在环中相遇,fast要比slow多走了x圈,一圈长为L个节点
- 那么从当前的的相遇的k节点走到入口节点需要多少步呢?
- 从上式我们可以推断从当前节点走到入口节点需要转(1-x)圈再走M步,那么此时让fast从头节点开始一步一步走M步,下一次的相遇就是在入口处的节点。
源码
public class Node{
public int value;
public Node next;
public Node(int data){
this.value=data;
}
}
public Node getLoopNode(Node head){
if(head==null||head.next==null||head.next.next==null){
return null;
}
Node n1=head.next;//n1->slow
Node n2=head.next.next;//n2->fast
while(n1!=n2){
if(n2.next==null||n2.next.next==null){
return null;
}
n2=n2.next.next;
n1=n1.next;
}
n2=head;//n2->walk again from head
while(n1!=n2){
n2=n2.next;
}
return n1;
}