leetcode 142. 环形链表II JavaScript版

484 阅读3分钟

「这是我参与11月更文挑战的第 2 天,活动详情查看:2021最后一次更文挑战

题目来源:LeetCode-142. 环形链表

题目

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

为了表示给定链表中的环,我们使用整数 pos 来表示链表尾连接到链表中的位置(索引从 0 开始)。 如果 pos 是 -1,则在该链表中没有环。注意,pos 仅仅是用于标识环的情况,并不会作为参数传递到函数中。

示例 1:

image.png

输入: head = [3,2,0,-4], pos = 1
输出: 返回索引为 1 的链表节点
解释: 链表中有一个环,其尾部连接到第二个节点。

示例 2:

image.png

输入: head = [1,2], pos = 0
输出: 返回索引为 0 的链表节点
解释: 链表中有一个环,其尾部连接到第一个节点。

示例 3:

输入: head = [1], pos = -1
输出: 返回 null
解释: 链表中没有环。

题解

通过上篇文章的介绍我们知道了如何证明链表中存在环。这次我们在上篇基础上实现返回链表开始入环的第一个节点。

解法

  • 由于[通过上篇文章的介绍]的解法一中通过遍历链表方式把每一个节点都通过set方式到Map对象中。
  • set之前先通过has方式判断Map对象中是否存在该结点,这是直接返回此节点即可。

代码实现

/**
 * @param {ListNode} head
 * @return {ListNode}
 */
var detectCycle = function(head) {
  let pointer = head;
  let map = new Map();
  while (pointer !== null) {
    if (map.has(pointer)) {
      return pointer; 
    } else {
      map.set(pointer);
      pointer = pointer.next; 
    }
  }
  return null; 
};

空间复杂度

上述代码中的Map对象由于Map对象所占用的空间是受到链表影响的空间复杂度为O(N)

空间复杂度

上述代码中通过while遍历链表是受到链表长度影响的时间复杂度为O(N)

进阶

你能用 O(1) (即,常量)内存解决此问题吗?

提出问题

通过上篇文章的介绍我们只能判断链表中是否存在环,哪么我们如何确定入环的第一个节点呢?

分析

  1. fastslow同时从头节点出发,fast走俩步slow走一步 0ccdac8072ad703f7269247f66eb140.png
  2. 第一次执行结果如下

dbea11eadec3006ba27f5753dbeea9f.png

  1. 第二次执行结果如下

5bf62bcb619a190046a35804cc3443b.png 4. 第三次执行结果如下

6a8b7a7cb7e61d6e7f842e1f059035b.png

  1. 第四次执行结果如下

48aecf1723d0bcabbd2d42a6fcc025c.png

  1. 第五次执行结果如下

8307a9b9168e86e487ac8938d5fa265.png

  1. 从上述五次执行结果来看,在绿色节点处fastslow再次相遇了

6b4194604ec373a82bbaaabd081a448.png

  • 我们把头节点到入环的第一个节点看成A
  • 把入环的第一个节点到fastslow相遇的节点看成B
  • fastslow相遇的节点到入环的第一个节点看成C
  • 也就是入上述图所示

fast: a+n(b+c)+b
slow: a+b
其中n代表fast所走的圈数,也就是说n >= 1

结合上面所述的所以内容我们可以得出一个公式

2(a+b)=a+n(b+c)+b 其中n >= 1
换算得:a=nc+(n-1)b 换算得:a=(n-1)(b+c)+c

  • 依据上图来看,n为1圈
  • 再次等到 a=c

到此可能有人会说,随着入环前所在的次数与入环所走的次数 n肯定是会变的。也就是说n不一定是1的时候才会相遇。 其实来说不管入环前所在的次数与入环所走的次数也不管n的为几,总会出现上述情况的,大家可以下去自己试一下。

通过 a=c 来看,只要我们让一个指针temphead出发每走一步,让slow继续走下去,他们最终会在第一个入环点相遇。

代码实现

/**
 * @param {ListNode} head
 * @return {ListNode}
 */
 var detectCycle = function(head) {
    if(head == null || head.next == null) return null;
    let fast = head;
    let slow = head;
    while(fast !== null && fast.next !== null){
        fast = fast.next.next;
        slow = slow.next;
        if(fast == slow){
            let temp = head;
            while(temp !== fast){
                fast = fast.next;
                temp = temp.next;
            }
            return  fast;
        }
    }
    return null;
};