返回

LeetCode 138:复制带随机指针的副本

前端

复制带随机指针的链表:深入剖析 LeetCode 138

导言

在软件开发中,处理具有复杂结构的数据结构是不可避免的。其中一个具有挑战性的数据结构是带有随机指针的链表。它与传统的链表不同,其中每个节点有一个指向下一个节点的 next 指针,而是每个节点还有一个指向链表中任意节点(或空节点)的 random 指针。复制这样的链表是一个棘手的问题,在 LeetCode 138 难题中得到了体现。本博客将深入探讨解决此难题的两种有效方法,并通过代码示例进行详细说明。

方法 1:哈希表

原理

此方法利用哈希表来存储已复制节点的映射关系。我们遍历原始链表,将每个节点作为键,其复制节点作为值存储在哈希表中。然后,我们再次遍历原始链表,更新每个节点的 next 和 random 指针。对于 next 指针,我们在哈希表中查找复制节点的 next 指针。对于 random 指针,我们在哈希表中查找复制节点的 random 指针。这种方法的优点是它易于实现且时间复杂度为 O(n),其中 n 是链表的长度。

代码示例

def copyRandomList(head):
    # 创建一个哈希表
    node_map = {}

    # 遍历原始链表,复制每个节点
    curr = head
    while curr:
        node = RandomListNode(curr.val)
        node_map[curr] = node
        curr = curr.next

    # 更新 next 和 random 指针
    curr = head
    while curr:
        node = node_map[curr]
        node.next = node_map.get(curr.next)
        node.random = node_map.get(curr.random)
        curr = curr.next

    # 返回复制的链表
    return node_map[head]

方法 2:拆分链表

原理

此方法通过拆分原始链表来创建复制的链表。我们遍历原始链表,并在每个节点后面插入一个复制的节点。然后,我们更新所有复制节点的 next 指针,使其指向原始节点的 next 指针。接下来,我们更新所有复制节点的 random 指针,使其指向原始节点的 random 指针。最后,我们将复制的链表与原始链表拆分,返回复制的链表头节点。这种方法的时间复杂度也为 O(n),但空间复杂度为 O(1),因为我们不需要额外的哈希表。

代码示例

def copyRandomList(head):
    # 复制每个节点
    curr = head
    while curr:
        next = curr.next
        copy = RandomListNode(curr.val)
        curr.next = copy
        copy.next = next
        curr = next

    # 更新 next 和 random 指针
    curr = head
    while curr:
        if curr.next:
            curr.next.next = curr.random.next
        curr = curr.next.next

    # 拆分链表
    copy = head.next
    curr = head
    while curr:
        next = curr.next.next
        curr.next = next
        curr = next
    return copy

结论

复制带有随机指针的链表是一个常见的数据结构挑战,在 LeetCode 138 难题中得到了体现。使用哈希表或拆分链表的方法都可以有效地解决此问题,每种方法都有其优点和缺点。了解这两种方法至关重要,以便选择最适合特定用例的方法。

常见问题解答

  1. 哪种方法更有效率?
    对于较小的链表,哈希表方法可能更有效率,因为它避免了额外空间的使用。但是,对于较大的链表,拆分链表方法可能更有效率,因为它避免了哈希表查找的开销。

  2. 哪种方法的空间复杂度更低?
    拆分链表方法的空间复杂度为 O(1),而哈希表方法的空间复杂度为 O(n),因为需要额外的空间来存储哈希表。

  3. 如何处理环形链表?
    这两种方法都不能处理环形链表,因为它们都会导致无限循环。

  4. 是否有通用的方法来处理所有类型的链表?
    不,没有一个通用的方法可以处理所有类型的链表。对于具有复杂结构的链表(例如交叉链表),可能需要使用不同的方法。

  5. 何时应该考虑使用哈希表或拆分链表方法?
    哈希表方法适用于较小或中等大小的链表,而拆分链表方法适用于较大或具有复杂结构的链表。