难度:中等
给定一个链表,返回链表开始入环的第一个节点。 如果链表无环,则返回 null。
为了表示给定链表中的环,我们使用整数 pos 来表示链表尾连接到链表中的位置(索引从 0 开始)。 如果 pos 是 -1,则在该链表中没有环。注意,pos 仅仅是用于标识环的情况,并不会作为参数传递到函数中。
说明:不允许修改给定的链表。
进阶:
你是否可以使用 O(1) 空间解决此题?
解题思路
可以考虑使用快慢指针来解决。
使用快慢指针,一个用slow标记,另一个用fast,slow每次移动一位,fast每次移动两位。如果存在入环点,那么必定在环内 slow 和 fast 会相遇。
假如存在入环点P,那么起点到入环点的距离为 A, 入环点到相遇点的距离为 B,相遇点到入环点的另一边的距离为 C。并且fast的速度是slow的两倍,综合以上可得等式:
A + N(B + C) + B = 2 * (A + B)
=> A = (B + C)(N - 1) + C
那么从等式可以看出,如果走上 N - 1 圈的环形后,再走上 C 那段距离 的话,就能再一次到达入环点,那么考虑引入另一个从起点开始的指针。
题解
public ListNode detectCycleSlowFastPointer(ListNode head) {
if (head == null) {
return null;
}
ListNode slow = head;
ListNode fast = head;
while (fast != null) {
slow = slow.next;
if (fast.next != null) {
fast = fast.next.next;
} else {
return null;
}
if (slow == fast) {
ListNode node = head;
while (node != slow) {
node = node.next;
slow = slow.next;
}
return node;
}
}
return null;
}
测试
LinkedListCycleII linkedListCycleII = new LinkedListCycleII();
private ListNode generateListNode(int[] node, int entryPoint) {
if (node.length == 0) {
return null;
}
ListNode listNode = new ListNode(node[0]);
ListNode entryPointListNode = entryPoint >= 0 ? listNode : null;
ListNode temp = listNode;
for (int index = 1, length = node.length; index < length; ++index) {
temp.next = new ListNode(node[index]);
temp = temp.next;
if (index == entryPoint) {
entryPointListNode = temp;
}
}
temp.next = entryPointListNode;
return listNode;
}
@Test
public void test_slowFastPointer_case1() {
ListNode head = generateListNode(new int[]{3, 2, 0, -4}, 1);
Assertions.assertEquals(2, linkedListCycleII.detectCycleSlowFastPointer(head).val);
}
@Test
public void test_slowFastPointer_case2() {
ListNode head = generateListNode(new int[]{1, 2}, 0);
Assertions.assertEquals(1, linkedListCycleII.detectCycleSlowFastPointer(head).val);
}
@Test
public void test_slowFastPointer_case3() {
ListNode head = generateListNode(new int[]{1}, -1);
Assertions.assertNull(linkedListCycleII.detectCycleSlowFastPointer(head));
}