返回

LeetCode 141:解开环形链表的秘密

后端

邂逅环形链表

环形链表是一种特殊的数据结构,它与普通链表的不同之处在于,链表的尾节点指向的不是空,而是链表中的某个节点,从而形成一个闭环。环形链表在编程中经常被用来解决特定问题,如判断链表是否有环、查找环的入口节点等等。

LeetCode 141 题正是考察了环形链表的基本性质。题目给定一个链表的头节点 head,要求判断链表中是否存在环。如果链表中有某个节点,可以通过连续跟踪 next 指针再次到达,则链表中存在环。

破解之法

面对环形链表,我们可以采用多种方法来判断其是否存在环。以下是最常见的两种方法:

1. 龟兔赛跑法

这种方法的灵感来源于著名的龟兔赛跑故事。我们使用两个指针,分别命名为「乌龟」和「兔子」。「乌龟」每次移动一步,而「兔子」每次移动两步。如果「兔子」在赛跑过程中追上了「乌龟」,则说明链表中存在环。这是因为「兔子」比「乌龟」跑得快,如果链表中没有环,那么「兔子」最终会跑到链表的末尾,而「乌龟」则会慢悠悠地跟在后面。但是,如果链表中存在环,那么「兔子」就会在环中不断循环,最终追上「乌龟」。

def has_cycle(head):
  """
  判断链表中是否存在环。

  参数:
    head: 链表的头节点。

  返回:
    如果链表中存在环,返回 True;否则,返回 False。
  """

  # 如果链表为空,则不存在环。
  if not head:
    return False

  # 设置两个指针,分别命名为「乌龟」和「兔子」。
  slow = head
  fast = head.next

  # 开始赛跑。
  while slow != fast:
    # 如果「兔子」到达了链表的末尾,则链表中不存在环。
    if not fast or not fast.next:
      return False

    # 「乌龟」每次移动一步。
    slow = slow.next

    # 「兔子」每次移动两步。
    fast = fast.next.next

  # 如果「乌龟」和「兔子」相遇,则链表中存在环。
  return True

2. 集合法

这种方法相对简单粗暴。我们使用一个集合来存储已经访问过的节点。当我们遍历链表时,如果发现某个节点已经存在于集合中,则说明链表中存在环。这是因为在没有环的情况下,我们只会在链表中遇到每个节点一次。

def has_cycle(head):
  """
  判断链表中是否存在环。

  参数:
    head: 链表的头节点。

  返回:
    如果链表中存在环,返回 True;否则,返回 False。
  """

  # 如果链表为空,则不存在环。
  if not head:
    return False

  # 创建一个集合来存储已经访问过的节点。
  visited = set()

  # 遍历链表。
  while head:
    # 如果当前节点已经存在于集合中,则链表中存在环。
    if head in visited:
      return True

    # 将当前节点添加到集合中。
    visited.add(head)

    # 移动到下一个节点。
    head = head.next

  # 如果遍历到链表的末尾,则链表中不存在环。
  return False

结语

环形链表是一个经典的数据结构问题,也是 LeetCode 中的常见题目。通过本文的讲解,相信你对环形链表有了一个更加深入的理解。掌握了判断环形链表的方法,你就可以轻松应对相关的问题,在编程面试中脱颖而出。