每日算法:142. 环形链表 II

131 阅读2分钟

难度:中等

给定一个链表,返回链表开始入环的第一个节点。 如果链表无环,则返回 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));
}