LeetCode 👉 HOT 100 👉 环形链表 - 简单题

665 阅读3分钟

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

题目

给你一个链表的头节点 head ,判断链表中是否有环。

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

如果链表中存在环 ,则返回 true 。 否则,返回 false 。

示例1

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

输出:true
解释:链表中有一个环,其尾部连接到第二个节点。

示例2

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

输出:false
解释:链表中没有环。

思路

按照题意,如果 head 链表中存在环,则在遍历链表时,总会在某个时机,遇到重复的节点

那么最常见的解法就是,遍历所有的节点,每次遍历到一个新节点的时候,判断当前节点是否被访问过;可以用 Set 结构存下所有已访问的节点,每次遍历到下一个节点的时候,如果这个节点能在 Set 结构中被找到,则说明 head 中存在环

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

    /**
     * @param {ListNode} head
     * @return {boolean}
     */
    var hasCycle = function(head) {
        const nodeSet = new Set();
        while (head) {
            if(nodeSet.has(head)) {
                return true;
            } else {
                nodeSet.add(head);
                head = head.next;
            }
        }
        return false;
    };

上面的解法,时间复杂度为 O(n),空间复杂度为 O(n),因为有可能需要完整的遍历链表,同时将所有的节点存入 Set 中,才能判断环的存在与否;

为了优化空间复杂度,还可以使用两个快慢指针,来处理这个问题;借图说明:

将一个指针初始化为 head 位置,定义为慢指针;一个初始化在 head.next 位置,定义为快指针;慢指针每次移动一步,快指针每次移动两步;那么显然,如果 head 中存在环,则快指针必然先进入环,然后慢指针也会进入环,由于快指针比慢指针走得快,必然会在某个时刻,快指针在环上会追上慢指针,也称为 相遇,这个时候 慢指针 == 快指针;如果不存在环,则快指针会优先到达链表的尾部,即 快指针.next == null;


    /**
     * @param {ListNode} head
     * @return {boolean}
     */
    var hasCycle = function(head) {
        if(!head || !head.next) return false;

        let slowPoint = head;
        let fastPoint = head.next;

        while (slowPoint != fastPoint) {
            if(fastPoint == null || fastPoint.next == null) {
                return false
            }

            // 快指针走两步,慢指针走一步
            fastPoint = fastPoint.next.next;
            slowPoint = slowPoint.next;
        }

        return true;
    };

小结

上述的快慢指针算法,也称为 Floyd 判圈算法;相对于第一种使用哈希表的解法,空间复杂度优化到了 O(1),但是时间复杂度更长了,因为哈希表的最长时间为 O(n),而快慢指针算法,在环存在的情况下,在慢指针进入环之后,依然还要进行快指针追上慢指针的步骤

算法的优化,两种常见的手段,空间换时间,例如 dfs回溯 时,常会用到的 记忆化 优化;两一种就是相对的 时间换空间,例如本题的 快慢指针 解法;

LeetCode 👉 HOT 100 👉 环形链表 - 简单题

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

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