142. 环形链表 II——哈希表、快慢指针(js)

84 阅读2分钟

题目描述

leetcode 142. 环形链表 II

解题思路

哈希表

哈希表的解题思路和 141. 环形链表 完全一样,最后找到节点的时候返回这个节点而不是返回true。

快慢指针

这边有个容易想不通的,快指针和慢指针的相遇点并不是环形链表的交点,而我们的任务是找交点。

在这里插入图片描述

1️⃣ 快慢指针相遇时的数学关系

假设:

  • 链表头节点 head 到入环点的距离为 a
  • 入环点到相遇点的距离为 b
  • 相遇点到入环点的剩余环长为 c(即环的总长度 L = b + c)。
  • 快指针 fast 走的步数是慢指针 slow 的 2 倍

假设快指针 绕环 n 后与慢指针相遇,则:

fast 走的距离=a+n(b+c)+b\text{fast 走的距离} = a + n(b + c) + b
slow 走的距离=a+b\text{slow 走的距离} = a + b

根据 快指针的步数是慢指针的 2 倍

2(a+b)=a+(n+1)b+nc2 (a + b) = a + (n+1) b + n c

整理得到:

a=c+(n1)(b+c)a = c + (n - 1)(b + c)

2️⃣ 关键推论

从上面的等式可以看出:

  • a head 到入环点的距离)
  • c (从相遇点到入环点的距离)
  • (n - 1)(b + c) (环的完整 (n - 1) 圈)

这意味着:

从相遇点到入环点的距离 c 加上 (n - 1) 圈的环长 恰好等于 head 到入环点的距离 a

完整代码

哈希表

var detectCycle = function(head) {
    // 哈希表
    let set = new Set()
    let p = head           // 指针从head开始
    while (p) {            // 遍历链表
    	if (set.has(p)) {  // 如果两个指针指向同一节点
    		return p       // 说明找到环形链表交点,返回该节点
    	} else {
    		set.add(p)     // 否则将当前节点加入Set
    		p = p.next     // 继续遍历
    	}
    }
    return null            // 遍历完链表还是没找到交点,返回null
};

快慢指针

var detectCycle = function(head) {
    // 快慢指针
    let slow = head                // 快慢指针都从head开始遍历链表
    let fast = head
    while (fast && fast.next) {    
        fast = fast.next.next      // 快指针一次走两步
        slow = slow.next           // 慢指针一次走一步
        if (fast === slow) {       // 如果快慢指针相遇
            let slow = head         // 将慢指针移动到头节点
            while (slow !== fast) { // 循环遍历 直到快慢指针再次相遇
                slow = slow.next    // 快慢指针每次都各走一步
                fast = fast.next
            }  
            return ptr              // 快慢指针相遇,返回当前指向的节点
        } 
    } 
    return null                    // 遍历完链表还是没找到交点,返回null
};