导航菜单

环形链表

🟢 简单

题目描述

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

如果链表中有某个节点,可以通过连续跟踪 next 指针再次到达,则链表中存在环。如果链表中存在环,则返回 true。否则,返回 false

示例 1

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

示例 2

输入:head = [1, 2], pos = 0
输出:true
解释:链表中有一个环,其尾部连接到第一个节点

示例 3

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

提示

  • 链表中节点的数目范围在 [0, 10^4]
  • -10^5 <= Node.val <= 10^5
  • pos-1 或者链表中的一个有效索引

解法一

参考答案 (2 个标签)
快慢指针 O(n)

思路

使用两个指针,慢指针每次走一步,快指针每次走两步。如果有环,快指针最终会追上慢指针。

代码实现

/**
 * @param {ListNode} head
 * @return {boolean}
 */
function hasCycle(head) {
    if (head === null || head.next === null) {
        return false;
    }
    
    let slow = head;
    let fast = head.next;
    
    while (slow !== fast) {
        if (fast === null || fast.next === null) {
            return false;
        }
        slow = slow.next;
        fast = fast.next.next;
    }
    
    return true;
}

复杂度分析

  • 时间复杂度:O(n)
  • 空间复杂度:O(1)

为什么快慢指针一定能相遇?

想象两个人在环形跑道上跑步,快的人速度是慢的人的两倍。快的人一定会追上慢的人。

解法二

参考答案 (2 个标签)
哈希表 O(n)

思路

遍历链表,用哈希表记录访问过的节点。如果遇到已访问的节点,说明有环。

代码实现

/**
 * @param {ListNode} head
 * @return {boolean}
 */
function hasCycle(head) {
    const visited = new Set();
    let curr = head;
    
    while (curr !== null) {
        if (visited.has(curr)) {
            return true;
        }
        visited.add(curr);
        curr = curr.next;
    }
    
    return false;
}

复杂度分析

  • 时间复杂度:O(n)
  • 空间复杂度:O(n)

搜索