LeetCode 142:环形链表 II

123 阅读3分钟

携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第2天,点击查看活动详情

LeetCode 142:环形链表II

题目描述

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

如果链表中有某个节点,可以通过连续跟踪 next 指针再次到达,则链表中存在环。 为了表示给定链表中的环,评测系统内部使用整数 pos 来表示链表尾连接到链表中的位置(索引从 0 开始)。如果 pos 是 -1,则在该链表中没有环。注意:pos 不作为参数进行传递,仅仅是为了标识链表的实际情况。
不允许修改链表。
例子

解题思路

思路一:利用Map、Set、WeakMap、WeakSet

在之前的一篇LeedCode 141:环形链表 - 掘金 (juejin.cn)中我们解决了环形链表问题,其中有一种解法是利用了ES6中WeakMap或WeakSet数据结构。
同样,这一道题目我们一样可以使用,这次我们使用Set,通过遍历及使用has(value)方法判断value值是否为Set的成员,如果是的话就说明有环,直接返回,否则使用add()方法向 Set 结构加入成员,因为 Set 结构不会添加重复的值 代码如下:

var detectCycle = function (head) {
  if (head === null) {
    return null;
  }
  const visited = new Set();
  while (head !== null) {
    if (visited.has(head)) {
      return head;
    }
    visited.add(head);
    head = head.next;
  }
  return null;
};

时间复杂度: O(n); 最坏情况下我们需要遍历每个节点一次

空间复杂度: O(n);

思路二:利用快慢指针

思路如下:

  1. 先判断有无环(设置快慢两个指针,遍历链表,快指针一次走两步,慢指针一次走一步,如果存在环,则快慢指针一定会指向同一节点,否则当快指针或快指针的下一个节点指向null时,则表明链表无环);

  2. 当快慢指针指向同一节点时,将快指针移动到head,快慢指针都一次走一步,相遇就返回快指针或慢指针;

其中第一步我们在 LeedCode 141:环形链表中已经实现过,很容易理解;
第二步理解:

如图, 设链表中环外部分长度为aslow指针进入环后,又走了b的距离与fast指针相遇。 此时,fast指针已经走完了环的n(n>=1)圈, 因此它走过的总距离为a+n(b+c)+b = a + (n+1)b + nc。 在任意时刻,fast指针走过的距离都为slow指针的2倍。因此:

a + (n+1)b + nc = 2(a + b) => a = c + (n -1)(b + c); 此时我们会发现: 如果从相遇点到入环点的距离加上n-1圈的环长,恰好等于从链表头部到入环点的距离。

实现代码如下:

var detectCycle = function (head) {
  if (head === null) {
    return null;
  }
  let slow = head, fast = head, isCycle = false;
  while(fast.next !== null && fast.next.next !== null) {
    fast = fast.next.next;
    slow = slow.next;
    if (fast === slow) {
      isCycle = true;
      break;
    }
  }
  if (!isCycle) {
    return null;
  }
  fast = head;
  while (fast !== slow) {
    fast = fast.next;
    slow = slow.next;
  }
  return fast;
}

时间复杂度: O(n);

空间复杂度: O(1);

参考资料

142. 环形链表 II - 力扣(LeetCode) (leetcode-cn.com)