返回

我用快慢指针解决LeetCode 142. Linked List Cycle II

后端

LeetCode 142:链表环检测和入口识别

概述

在计算机科学中,链表是一种广泛使用的线性数据结构,它由一系列通过指针相互连接的节点组成。链表环 是一种特殊情况,其中链表中某个节点指向前面某个节点,形成一个闭合的循环。检测链表环的存在对于数据结构的完整性至关重要。LeetCode 142 是一道经典算法题,它要求你判断一个链表中是否存在环路,如果存在,则返回环路的入口节点。

解题思路

解决 LeetCode 142 问题的常用方法有两种:使用集合和使用快慢指针。

1. 使用集合

这种方法利用集合的特性,即元素不重复且插入和查询的效率都很快,来记录访问过的节点。算法步骤如下:

  • 创建一个空集合 visited 来存储访问过的节点。
  • 从头节点开始遍历链表。
  • 对于每个访问的节点,如果它已经在 visited 集合中,则说明存在环路,并返回该节点。
  • 如果未找到环路,则将当前节点添加到 visited 集合中,并继续遍历链表。

代码示例:

def has_cycle_using_set(head):
  """
  :type head: ListNode
  :rtype: bool
  """
  visited = set()  # 用来记录访问过的节点
  while head:
    if head in visited:
      return True
    visited.add(head)
    head = head.next
  return False

2. 使用快慢指针

这种方法利用快慢指针来判断是否存在环路。快指针每次移动两步,慢指针每次移动一步。如果存在环路,则快指针和慢指针最终会相遇。相遇时,慢指针重新从头开始移动,快指针继续移动。当快指针再次与慢指针相遇时,相遇点就是环路入口节点。

代码示例:

def has_cycle_using_fast_slow_pointers(head):
  """
  :type head: ListNode
  :rtype: bool
  """
  if not head or not head.next:
    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

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

  return True

方法比较

方法 时间复杂度 空间复杂度
使用集合 O(n) O(n)
使用快慢指针 O(n) O(1)

结论

LeetCode 142 是一个经典算法题,要求判断一个链表中是否存在环路,如果存在,则返回环路的入口节点。可以使用集合或快慢指针这两种方法来解决这个问题。集合方法空间复杂度较低,但时间复杂度较高。快慢指针方法时间复杂度较低,但空间复杂度较高。你可以根据具体情况选择合适的方法。

常见问题解答

  1. 什么是链表环?

    链表环是指链表中某个节点指向前面某个节点,形成一个闭合的循环。

  2. 为什么检测链表环很重要?

    检测链表环很重要,因为它可以防止数据结构出现问题,例如无限循环或内存泄漏。

  3. 使用集合方法时,为什么需要记录访问过的节点?

    使用集合方法时,需要记录访问过的节点,因为当遇到已经访问过的节点时,说明存在环路。

  4. 为什么快慢指针方法的慢指针在相遇后需要重新从头开始移动?

    快慢指针方法的慢指针在相遇后需要重新从头开始移动,是为了确定环路入口节点。慢指针从头开始移动,快指针继续在环内移动。当快指针再次与慢指针相遇时,相遇点就是环路入口节点。

  5. 快慢指针方法什么时候会失败?

    快慢指针方法可能会失败,当链表中存在多个环路时。这种情况下,该方法将返回第一个环路的入口节点,而不是最后一个环路的入口节点。