LeetCode 👉 HOT 100 👉 环形链表 II - 中等题

1,263 阅读3分钟

「这是我参与2022首次更文挑战的第24天,活动详情查看:2022首次更文挑战」。

题目

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

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

不允许修改 链表。

示例1

输入:head = [3,2,0,-4], pos = 1

输出:返回索引为 1 的链表节点
解释:链表中有一个环,其尾部连接到第二个节点。

示例2

输入:head = [1], pos = -1

输出:返回 null
解释:链表中没有环。

思路

这道题是在上一篇文章的基础 LeetCode 👉 HOT 100 👉 环形链表 - 简单题 ,除了要判断链表中是否存在环,还要求入环的第一个节点,例1即为节点 2;

如果链表中存在环,在遍历链表时,遇到的第一个重复元素,即为入环节点;可以按照上一篇文章的思路,使用 Set 结构存储已访问的节点,返回第一个重复的节点即可

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

    /**
     * @param {ListNode} head
     * @return {ListNode}
     */
    var detectCycle = function(head) {
        const set = new Set();

        while (head) {
            // 返回重复的第一个节点
            if(set.has(head)) {
                return head;
            } else {
                set.add(head);
                head = head.next;
            }
        }

        return null;
    };

上面的解法,问题还是在空间复杂度太高,为 O(n); LeetCode 官网解法,使用了 slow, fast, ptr 三个指针,利用数学公示推导,也可以得到入环的节点,感兴趣的可以自己看看,传送门 ☞

这里提供另一种思路,假如链表中存在环,同时求得环的长度 len,那么对于倒数第 len 个节点,不就是入环节点么~

具体步骤如下:

  • 使用 slow、fast 两个指针,判断环是否存在;当环存在,在两个指针相遇时,保存相遇的节点为 meetNode,此时该结点一定在环内;环不存在则直接返回 null

  • 初始化 len = 0,让 meetNode 继续在环内遍历,每次遍历都进行 len++,结束条件为 meetNode == fast,此时 len 即为环的长度;

  • 再次使用 slow、fast 两个指针,初始化为头节点 head,让 fast 指针先走 len 步;再让两指针同时走,当 fast.next == slow 的时候,说明 fast 到达了尾节点,此时 slow 指针即指向入环节点。

    /**
     * @param {ListNode} head
     * @return {ListNode}
     */
    var detectCycle = function(head) {
        if(!head) return null;
        
        // 判断是否有环
        let slow = head, fast = head.next;
        while (slow != fast) {
            if(!fast || !fast.next) {
                return null;
            }

            slow = slow.next;
            fast = fast.next.next;
        }

        // 计算环的长度
        let len = 0, meetNode = fast;
        while(meetNode.next != fast) {
            meetNode = meetNode.next;
            len++;
        }

        // 重新初始化 快慢指针
        slow = head, fast = head;

        // 快指针先走
        while(len > 0) {
            fast = fast.next;
            len--;
        }

        // 当fast到达尾节点,尾节点的 next 会与 slow相等
        while(slow = !fast.next) {
            slow = slow.next;
            fast = fast.next;
        }

        // 当前的slow即为入环节点
        return slow;
    };

小结

如果实在没思路,就画图,往往规律就在画图的过程中,会暴漏出来。

对于链表的操作,双指针,往往能派上用场~

LeetCode 👉 HOT 100 👉 环形链表 II - 中等题

合集:LeetCode 👉 HOT 100,有空就会更新,大家多多支持,点个赞👍

如果大家有好的解法,或者发现本文理解不对的地方,欢迎留言评论 😄