26.环形链表2

71 阅读2分钟

题目链接

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

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

不允许修改 链表。

解法1 暴力解法

思路

和上一题的思路类似,上一题是检查是否有环,而现在题目要求返回环形的入口。

环形的入口其实就是哈希表里第一个重复的节点。直接返回即可。

代码

function detectCycle(head: ListNode | null): ListNode | null {
    let cur = head;
    const set = new Set();

    while (cur) {
        if (set.has(cur)) {
            return cur;
        } else {
            set.add(cur);
        }
        cur = cur.next;
    }

    return null;
};

时空复杂度

时间复杂度:O(n)

空间复杂度:O(n)

解法2 快慢指针

思路

判断是否有环的思路还是沿用上题,首先确认链表是有环的,然后再去思考如何找到环形的入口。

快慢指针会在环形内部相遇,而这个相遇的地点是不确定的,但可以确定的是快慢指针所走的步数。

慢指针走了入口前 x 步和环形内 y 步,即 (x + y) 步。而快指针走了它的两倍 2(x + y) 步。而快指针是在环内转圈遇到慢指针,所以它走的步数是 x + y 步加上在环内的圈数,假设 k 圈,环的长度为 C

我们可以得到 2(x + y) = x + y + K * C

移项可以得到 x + y = k * C。所以head指针到环形入口的 x = k * C - y

C 是整个环的长度,y 是相遇的节点即从环形入口开始走的步数,所以只要将剩下的步数走完,即可走到环形的入口。

此时让 head 指针和 slow 指针同时向后走,相遇的节点就是环形的入口节点。

代码

function detectCycle(head: ListNode | null): ListNode | null {
    if (!head || !head.next) {
        return null;
    }

    let slow = head;
    let fast = head;
    while (fast && fast.next) {
        slow = slow.next;
        fast = fast.next.next;

        if (slow === fast) {
            while (slow !== head) {
                slow = slow.next;
                head = head.next;
            }
            return slow;
        }
    }

    return null;
};

时空复杂度

时间复杂度:O(n)

空间复杂度:O(1)