s是慢指针(每次走一步),f是快指针(每次走两步)
假设环外有a个结点,环里有b个结点。当s走到环入口时,f已经在环内走了(n圈+x个)结点,距离环入口x个结点,
此时,f比s落后b-x个结点
从图中位置开始,s、f每走一次,两指针相差距离就减一(因为f比s多走一步)。
当s在环内走了b-x步后,f追上s
得到以下公式
快慢指针相遇时,经过的时间:t = a+b-x=(a + n*b + x + 2*(b-x) )/2
把右边两个式子化简:a = n*b + x
由上面化简后式子+图片可知:当让一个指针指向链表起点,另一个指针留在第一次相遇的结点处,
接下来两个指针每次都只走一步,当他们相遇时。此时就在环起点
public class Test {
public static void main(String[] args) {
//1-2-3-4-5-6-7-8
Node node8 = new Node(8, null);
Node node7 = new Node(7, node8);
Node node6 = new Node(6, node7);
Node node5 = new Node(5, node6);
Node node4 = new Node(4, node5);
Node node3 = new Node(3, node4);
Node node2 = new Node(2, node3);
Node node1 = new Node(1, node2);
//在5处成环
node8.next = node5;
boolean flag = false;//false没成环,true成环
//设置快慢指针,快指针一次走两个结点,慢指针一次走一个指针
// 如果有环,快指针在环内一定能追上慢指针
Node fast = node1;
Node slow = node1;
while (fast.next != null && fast.next.next != null) {//防止出现null.next,空指针异常
fast = fast.next.next;
slow = slow.next;
if (fast == slow) {
flag = true;
break;//快慢指针相遇
}
}
if (!flag) {
System.out.println("该链表无环");
return;
}
//找环起点,此时快慢指针相遇
slow = node1;
while (slow != fast) {
slow = slow.next;
fast = fast.next;
}
System.out.println("该链表有环,环起点为:" + slow.data);
}
}
class Node {
int data;
Node next;
public Node() {
}
public Node(int data, Node next) {
this.data = data;
this.next = next;
}
@Override
public String toString() {
return "{" + data +
", next=" + next +
'}';
}
}
备注:当慢指针第一次进入环内,一定没走完一圈就和快指针相遇,因为当慢指针第一次到环起点时,快指针比慢指针落后b-x个结点,接下来快慢指针每走一次,两指针相差距离就减一,当慢指针走了b-x步后,两指针相遇