环形链表II

264 阅读2分钟

正题

环形链表 II

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

如果链表中有某个节点,可以通过连续跟踪 next 指针再次到达,则链表中存在环。 为了表示给定链表中的环,评测系统内部使用整数 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

image.png

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

解析:

之前的文章中有提到过判断链表是否为环形链表,请参考文章 双指针龟兔赛跑判断链表是否成环

上述文章中主要用到了2个方法,一个是数组缓存法,另一个是不用开辟新的数组空间的 双指针龟兔赛跑算法,但龟兔赛跑方法只适于用判断是否成环,并不能直接得到成环的节点在第几个节点。原因是 “龟” 和 “兔” 未必在最后节点相遇,且相遇在第几个节点都未可知。所以应本题要求找到成环的节点,用这个方法是不合适的。

那么如何才能找到成环的节点呢?可以参考第一个方法,数组缓存法。

当链表遍历的同时,记录链表在数组中,当再次出现数组中元素的时候就表示该链表成环,且成环的节点就为数组最后一个元素。

同样的,我们可以使用更快地查找方法,用 Map 取代数组。

总体思路: 创建一个Map,将链表遍历,如果 map 中不存在那么 map.set,如果存在那么 return map.get,逻辑清晰,方法简单!!

应该不需要再做过多的解释,代码:

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

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