算法(JS):找环形链表的入环节点,空间复杂度O(1)解法详解

115 阅读1分钟

题目链接

142.环形链表II

题目描述

给定一个链表的头节点  head ,返回链表开始入环的第一个节点。 如果链表无环,则返回 null

分析

可令快慢指针fast, slow同时从head出发,fast每次前进 2 个节点,slow前进 1 个。若链表有环,则两指针必定相遇

图示如下: 环链表示意图.jpg

假设 起始节点入环节点 间的距离为 a入环点相遇点 间的距离为 b,环的 周长Cfast走过的总路程为S(fast)slow走过的总路程为S(slow)

由图可知:

  • S(slow) = a + b
  • S(fast) = a + b + kCk为常数,且 k >= 1

S(fast) = 2 * S(slow),可得等式a + b + kC = 2 (a + b)

移项化简,可得: a = (k - 1)C + (C - b)

由此等式可知:从起始点走到入环点的距离a从相遇点走k - 1圈再走到入环点的距离 相等

即:令两指针分别从 起始点快慢指针相遇点 出发,两指针每次都只走 1 步,最终 相遇时所在节点 即为 入环节点

解法

  1. fast, slow两指针同时从head出发
  2. fast走到了null则证明无环。
  3. fastslow相遇时,令新指针target = headtargetslow此后每次各向前走 1 步,两指针相遇时所在节点即为所求。

代码

/**
 * Definition for singly-linked list.
 * function ListNode(val) {
 *     this.val = val;
 *     this.next = null;
 * }
 */

/**
 * @param {ListNode} head
 * @return {ListNode}
 */
var detectCycle = function(head) {
    let fast = head,
        slow = head,
        target = head;
    while(fast) {
        fast = fast.next?.next;
        slow = slow.next;
        if(fast === slow) {
            // 有环
            while(slow !== target) {
                slow = slow.next;
                target = target.next;
            }
            return target;
        }
    }
    // 无环
    return null
};