返回

解码算法秘方,轻松识别链表里的环与入口

后端

前言:链表与环结构
链表是一种常见的数据结构,它由一系列节点组成,每个节点包含数据和指向下一个节点的指针。如果最后一个节点的指针指向第一个节点,那么这个链表就形成了一个环。这种环形结构在实际开发中很常见,例如循环列表、图的邻接表等。

检测环的存在

哈希法法:

哈希法是检测链表中是否存在环的一种简单而有效的方法。我们可以将链表中的每个节点存储在一个哈希表中,如果我们遇到一个已经在哈希表中的节点,则表明链表中存在环。哈希法的优点是实现简单,时间复杂度为 O(n),空间复杂度为 O(n)。

双指针法:

双指针法是一种更加巧妙的方法来检测链表中是否存在环。我们可以使用两个指针,一个称为快指针,另一个称为慢指针。快指针每次移动两个节点,而慢指针每次移动一个节点。如果链表中存在环,那么快指针最终会赶上慢指针。双指针法的时间复杂度为 O(n),空间复杂度为 O(1)。

查找环的入口位置

三指针法:

一旦我们确定链表中存在环,我们就可以使用三指针法来找到环的入口位置。我们可以使用三个指针,一个称为起点指针,一个称为快指针,另一个称为慢指针。起点指针从链表的头部开始,快指针和慢指针从环中任意一个节点开始。快指针每次移动两个节点,慢指针每次移动一个节点。当快指针和慢指针相遇时,它们将位于环的入口位置。三指针法的时间复杂度为 O(n),空间复杂度为 O(1)。

算法演示

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

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

    返回:
        True 如果链表中存在环,否则返回 False。
    """
    # 使用哈希表来存储已经访问过的节点
    visited = set()

    # 从头结点开始遍历链表
    while head:
        # 如果当前节点已经在哈希表中,则表明链表中存在环
        if head in visited:
            return True

        # 将当前节点添加到哈希表中
        visited.add(head)

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

    # 如果遍历完整个链表,没有发现环,则返回 False
    return False


def find_cycle_entrance(head):
    """
    找到链表中环的入口位置。

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

    返回:
        环的入口位置,如果链表中没有环,则返回 None。
    """
    # 如果链表中不存在环,则返回 None
    if not has_cycle(head):
        return None

    # 使用三个指针来找到环的入口位置
    start = head
    fast = head
    slow = head

    # 快指针每次移动两个节点,慢指针每次移动一个节点
    while fast and fast.next:
        fast = fast.next.next
        slow = slow.next

        # 如果快指针和慢指针相遇,则表明找到了环的入口位置
        if fast == slow:
            break

    # 将快指针移动到链表的头部
    fast = head

    # 快指针和慢指针同时移动,直到它们相遇
    while fast != slow:
        fast = fast.next
        slow = slow.next

    # 返回环的入口位置
    return fast

结语

掌握了检测环的存在和查找环的入口位置的算法后,你就可以轻松应对这类问题了。这些算法在实际开发中非常有用,例如调试循环列表、图的邻接表等数据结构。希望你能够学以致用,在自己的编程实践中灵活运用这些算法。