思路
快指针fast和慢指针slow同时从头结点出发,快指针一次走两步,慢指针一次走一步,假设第一次相遇的点为C.
相遇时,慢指针保持不变,快指针重新从头结点出发,每次走一步,再次相遇时,则为环的入口结点B。
慢指针进入环的第一圈就会和快指针相遇(假设慢指针第一次到达入口结点B时,快指针一定已经在环内,假设快指针此时距离B点距离为L,L一定小于环的长度,因此当慢指针进入环走了L时,快指针一定能走2L追上慢指针。)
假设环的长度为r,CBD的长度为P,则当第一次相遇时有
2(X+Y)= X+nr+Y,即X+Y=nr= (n-1)r+r= (n-1)r+P+Y,所以X=(n-1)r+P。
也就是头结点到环的入口结点的距离= 相遇点到环的入口结点间的距离+(n-1)个环的距离
因此,当相遇后快指针重新从头结点重新出发时,二者终将在环的入口结点处相遇。
/*
public class ListNode {
int val;
ListNode next = null;
ListNode(int val) {
this.val = val;
}
}
*/
public class Solution {
public ListNode EntryNodeOfLoop(ListNode pHead)
{
if(pHead == null || pHead.next == null){
return null;
}
ListNode fast = pHead;
ListNode slow = pHead;
while(fast != null && fast.next != null){
fast = fast.next.next;
slow = slow.next;
if(fast == slow){
fast = pHead;
while(fast != slow){
fast = fast.next;
slow = slow.next;
}
return fast;
}
}
return null;
}
}
代码
/*
public class ListNode {
int val;
ListNode next = null;
ListNode(int val) {
this.val = val;
}
}
*/
public class Solution {
public ListNode EntryNodeOfLoop(ListNode pHead)
{
if(pHead == null || pHead.next == null){
return null;
}
ListNode fast = pHead;
ListNode slow = pHead;
while(fast != null && fast.next != null){
fast = fast.next.next;
slow = slow.next;
if(fast == slow){
fast = pHead;
while(fast != slow){
fast = fast.next;
slow = slow.next;
}
return fast;
}
}
return null;
}
}
思路2
Set,从头结点开始遍历,将遍历的结点依次存入Set集合中,第一个重复结点就是环的入口结点。
思路3
判断是否有环:双指针能否相遇
环的入口结点:环的长度,从头结点开始走环的长度就是环的入口结点
/*
public class ListNode {
int val;
ListNode next = null;
ListNode(int val) {
this.val = val;
}
}
*/
public class Solution {
public ListNode EntryNodeOfLoop(ListNode pHead)
{
if(pHead == null){
return null;
}
// 1.判断链表中有环
ListNode l=pHead,r=pHead;
boolean flag = false;
while(r != null && r.next!=null){
l=l.next;
r=r.next.next;
if(l==r){
flag=true;
break;
}
}
if(!flag){
return null;
}else{
// 2.得到环中节点的数目
int n=1;
r=r.next;
while(l!=r){
r=r.next;
n++;
}
// 3.找到环中的入口节点
l=r=pHead;
for(int i=0;i<n;i++){
r=r.next;
}
while(l!=r){
l=l.next;
r=r.next;
}
return l;
}
}
}