返回

重温 Leetcode 经典题之 142. 检测环形链表 II

前端

环形链表:判断和处理方法详解

什么是环形链表?

环形链表是一种链表,其中某个节点指向链表中的另一个节点,形成一个闭合的环路。在编程中,环形链表可能导致程序陷入死循环或其他异常行为,因此判断和处理它们至关重要。

两种环形链表判断算法

Floyd 判圈算法

原理:

Floyd 判圈算法使用两个指针,快指针和慢指针,以不同的速度遍历链表。如果链表中存在环,快指针最终将追上慢指针,相遇的节点就是环的入口节点。

步骤:

  1. 初始化快指针和慢指针,均指向链表头部。
  2. 快指针每次移动两步,慢指针每次移动一步。
  3. 重复步骤 2,直到快指针和慢指针相遇或快指针到达链表尾部。
  4. 如果快指针和慢指针相遇,则链表中存在环,相遇的节点就是环的入口节点。

代码示例:

def detect_cycle(head):
    slow = head
    fast = head

    while fast and fast.next:
        slow = slow.next
        fast = fast.next.next

        if slow == fast:
            break

    if slow != fast:
        return None

    slow = head

    while slow != fast:
        slow = slow.next
        fast = fast.next

    return slow

双指针法

原理:

双指针法也使用两个指针,指针 1 和指针 2,以相同的速度遍历链表。如果链表中存在环,指针 1 和指针 2 最终将相遇,相遇的节点就是环的入口节点。

步骤:

  1. 初始化指针 1 和指针 2,均指向链表头部。
  2. 指针 1 和指针 2 同时移动一步。
  3. 重复步骤 2,直到指针 1 和指针 2 相遇或指针 1 或指针 2 达到链表尾部。
  4. 如果指针 1 和指针 2 相遇,则链表中存在环,相遇的节点就是环的入口节点。

代码示例:

def detect_cycle(head):
    ptr1 = head
    ptr2 = head

    while ptr1 and ptr2 and ptr2.next:
        ptr1 = ptr1.next
        ptr2 = ptr2.next.next

        if ptr1 == ptr2:
            break

    if ptr1 != ptr2:
        return None

    ptr1 = head

    while ptr1 != ptr2:
        ptr1 = ptr1.next
        ptr2 = ptr2.next

    return ptr1

算法的时空复杂度分析

Floyd 判圈算法和双指针法的时空复杂度均为 O(n),其中 n 为链表的长度。这是因为这两种算法都需要遍历整个链表一次。

结论

环形链表的判断和处理是编程中的一项重要技能。Floyd 判圈算法和双指针法是两种经典的算法,它们简单高效,易于理解和实现。掌握这些算法,可以帮助你轻松解决环形链表相关的问题。

常见问题解答

1. 如何确定环的长度?

找到环的入口节点后,可以遍历环,并计算节点的数量,以获得环的长度。

2. 如何删除环?

要删除环,可以找到环的入口节点,然后将环中最后一个节点的 next 指针设置为 null。

3. 环形链表有什么实际应用?

环形链表可用于实现循环缓冲区、LRU 缓存和其他需要循环数据结构的情况。

4. Floyd 判圈算法和双指针法的区别是什么?

Floyd 判圈算法使用不同的移动速度,而双指针法使用相同的移动速度。Floyd 判圈算法可以更快地找到环,但双指针法更容易实现。

5. 如何判断一个链表是否是环形链表?

使用 Floyd 判圈算法或双指针法可以判断一个链表是否是环形链表。如果算法找到了环,则链表是环形链表,否则不是。