/**
* 检查链表是否存在环,并计算环的长度以及环的起点
*
* 判断一个链表是否存在环有一个简单的方法,就是使用一个快指针、和一个慢指针,快指针每次走两步,慢指针每次走一步,
* 则如果有环,它们最后必然会相遇的。
* 本题的难点在于要找出环的起点。其实也不难,与判断是否有环类似,用两个步长分别为1和2的指针遍历链表,直到两者相遇,此时慢指针走过的长度就是环的长度。
* 另外相遇后把其中指针重新设定为起始点,让两个指针以步长1再走一遍链表,相遇点就是环的起始点。
*
* 证明也很简单,证明如下:
*
* 我们注意到第一次相遇时
*
* 慢指针走过的路程S1 = 非环部分长度 + m * 环长 + 弧A长 (m:环的圈数)
*
* 快指针走过的路程S2 = 非环部分长度 + n * 环长 + 弧A长 (n:环的圈数)
*
*
* S1 * 2 = S2,可得:非环部分长度 + 弧A长 = (n-2m) * 环长
* m,n为整数 非环部分长度 + 弧A长最小值为环长的一倍
* 让指针A回到起始点后,走过一个非环部分长度,指针B走过了相等的长度,也就是n-2m * 环长 - 弧A长,正好回到环的开头。
*
*/
/**
* 链表结构
*/
public static class ListNode {
// next指针
ListNode next;
// value值
int value;
// 构造方法
public ListNode(int val) {
next = null;
value = val;
}
}
/**
* 检查链表是否存在环,并计算环的长度以及环的起点
*
* 判断一个链表是否存在环有一个简单的方法,就是使用一个快指针、和一个慢指针,快指针每次走两步,慢指针每次走一步,
* 则如果有环,它们最后必然会相遇的。
* 本题的难点在于要找出环的起点。其实也不难,与判断是否有环类似,用两个步长分别为1和2的指针遍历链表,直到两者相遇,此时慢指针走过的长度就是环的长度。
* 另外相遇后把其中指针重新设定为起始点,让两个指针以步长1再走一遍链表,相遇点就是环的起始点。
*
* 证明也很简单,证明如下:
*
* 我们注意到第一次相遇时
*
* 慢指针走过的路程S1 = 非环部分长度 + m * 环长 + 弧A长 (m:环的圈数)
*
* 快指针走过的路程S2 = 非环部分长度 + n * 环长 + 弧A长 (n:环的圈数)
*
*
* S1 * 2 = S2,可得 非环部分长度 = (n-2m) * 环长 - 弧A长
*
* 让指针A回到起始点后,走过一个非环部分长度,指针B走过了相等的长度,也就是n-2m * 环长 - 弧A长,正好回到环的开头。
* @param args
*/
public static void main(String[] args) {
ListNode head = new ListNode(0);
ListNode node1 = new ListNode(1);
ListNode node2 = new ListNode(2);
ListNode node3 = new ListNode(3);
ListNode node4 = new ListNode(4);
ListNode node5 = new ListNode(2);
ListNode node6 = new ListNode(3);
ListNode node7 = new ListNode(4);
head.next = node1;
node1.next = node2;
node2.next = node3;
node3.next = node4;
node4.next = node5;
node5.next = node6;
node6.next = node7;
// 指向node4
node7.next = node4;
// 环是否存在
boolean circle = false;
// 使用快慢指针
ListNode faster = head.next.next;
ListNode slower = head.next;
while (faster != null && slower != null) {
if (faster.equals(slower)) {
System.out.println("circle is exist");
circle = true;
break;
}
// 防止没有环到最后了
if (faster.next == null) {
System.out.println("circle is not exist");
break;
}
faster = faster.next.next;
slower = slower.next;
}
// 求环的长度:
// 当两个相遇时,慢指针继续向前移动,faster指针不动,
// 慢指针移动移动长度就是环的长度
if (circle) {
// 当存在环,
int count = 0;
slower = slower.next;
while (slower != null) {
count ++;
if (faster.equals(slower)) {
System.out.println("circle length:" + count);
break;
}
slower = slower.next;
}
}
// 求环的开始节点
// 慢指针继续回到初始点,让两个指针以步长1再走一遍链表,相遇点就是环的起始点
if (circle) {
// 当存在环,
slower = head;
while (slower != null) {
if (faster.equals(slower)) {
System.out.println("circle start Node:" + slower.value);
break;
}
slower = slower.next;
faster = faster.next;
}
}
}