python-leetcode 32.随机链表的复制

题目:

给定一个长度为n的链表,每个节点包含一个额外增加的随机指针random,该指针可以指向链表中的任何节点或空节点。

构造这个链表的深拷贝。深拷贝应该正好由n个全新节点组成,其中每个新节点的值都设为其对应的原节点的值。新节点的next指针和random指针也都应指向复制链表中的新节点,并使原链表和复制链表中的这些指针能够表示相同的链表状态。复制链表中的指针都不应指向原链表中的节点。

例如,如果原链表中有 X 和 Y 两个节点,其中 X.random --> Y 。那么在复制链表中对应的两个节点 x 和 y ,同样有 x.random --> y 。

返回复制链表的头节点。

用一个由n个节点组成的链表来表示输入/输出链表。每个节点用一个[val,random_index]

表示:

val:一个表示node.val的整数

random_index:随机指针指向的节点索引(范围从0到n-1);如果不指向任何节点,则为null.

代码只接受原链表的头节点head作为传入参数。

python-leetcode 32.随机链表的复制_第1张图片

方法一:回溯+哈希表

要求对一个特殊的链表进行深拷贝。如果是普通链表,可以直接按照遍历的顺序创建链表节点。本题中因为随机指针的存在,当拷贝节点时,「当前节点的随机指针指向的节点」可能还没创建,需要变换思路。

一个可行方案是,利用回溯的方式,让每个节点的拷贝操作相互独立,对于当前节点,首先要进行拷贝,然后我们「当前节点的后继节点」和「当前节点的随机指针指向的节点」拷贝,拷贝完成后将创建的新节点的指针返回,即可完成当前节点的两指针的赋值。

具体地,用哈希表记录每一个节点对应新节点的创建情况。遍历该链表的过程中,检查「当前节点的后继节点」和「当前节点的随机指针指向的节点」的创建情况。如果这两个节点中的任何一个节点的新节点没有被创建,都立刻递归地进行创建。当拷贝完成,回溯到当前层时,即可完成当前节点的指针赋值。

注意一个节点可能被多个其他节点指向,因此我们可能递归地多次尝试拷贝某个节点,为了防止重复拷贝,我们需要首先检查当前节点是否被拷贝过,如果已经拷贝过,我们可以直接从哈希表中取出拷贝后的节点的指针并返回即可。

在实际代码中,我们需要特别判断给定节点为空节点的情况。

python-leetcode 32.随机链表的复制_第2张图片

"""
# Definition for a Node.
class Node:
    def __init__(self, x, next=None, random=None):
        self.val = int(x)
        self.next = next
        self.random = random
"""

class Solution(object):
    def __init__(self):
        self.cached_node = {}  # 将 cached_node 字典作为实例属性

    def copyRandomList(self, head):
        """
        :type head: Node
        :rtype: Node
        """
        if not head:
            return None
        
        if head not in self.cached_node:  # 如果当前节点没有被复制过
            # 创建一个新节点,值与原节点相同
            head_new = Node(head.val)
            self.cached_node[head] = head_new  # 缓存当前节点和新节点的映射

            # 递归复制 next 和 random 指针
            head_new.next = self.copyRandomList(head.next)
            head_new.random = self.copyRandomList(head.random)
        
        return self.cached_node[head]  # 返回缓存中的新节点

cached_node = {节点0: 新节点0, 节点1: 新节点1, 节点2: 新节点2, 节点4: 新节点4, 节点3: 新节点3}

时间复杂度:O(n)

空间复杂度:O(n)


方法二:迭代+节点拆分

python-leetcode 32.随机链表的复制_第3张图片

例如链表 1→2→3,依次复制每个节点(创建新节点并复制 val 和 next),把新节点直接插到原节点的后面,形成一个交错链表:1→1 ′→2→2 ′ →3→3 ′ 如此一来,原链表节点的下一个节点,就是其对应的新链表节点了!然后遍历这个交错链表,假如节点 1 的 random 指向节点 3,那么就把节点 1 ′  的 random 指向节点 3 的下一个节点 3 ′ ,这样就完成了对 random 指针的复制。

"""
# Definition for a Node.
class Node:
    def __init__(self, x, next=None, random=None):
        self.val = int(x)
        self.next = next
        self.random = random
"""

class Solution(object):
    def copyRandomList(self, head):
        """
        :type head: Node
        :rtype: Node
        """
        if not head:  # 判断输入链表是否为空
            return None
        
        # 第一步:复制节点并将新节点插入到原节点后
        cur = head
        while cur:
            # 在原节点后插入一个新的节点
            new_node = Node(cur.val)
            new_node.next = cur.next
            cur.next = new_node
            cur = new_node.next  # 跳过新节点,继续遍历原链表

        # 第二步:处理新节点的random指针
        cur = head
        while cur:
            if cur.random:  # 如果当前节点有random指针
                cur.next.random = cur.random.next  # 新节点的random指针指向原节点random的下一个节点
            cur = cur.next.next  # 跳过新节点,继续处理下一个原节点

        # 第三步:分离原链表和新链表
        cur = head
        new_head = head.next  # 新链表的头节点
        while cur:
            new_node = cur.next  # 获取当前新节点
            cur.next = new_node.next  # 还原原链表的next指针
            if new_node.next:
                new_node.next = new_node.next.next  # 更新新节点的next指针
            cur = cur.next  # 移动到下一个原链表节点

        return new_head  # 返回新链表的头节点
        

时间复杂度:O(N)遍历链表三次

空间复杂度:O(1)返回值不计入空间复杂度

你可能感兴趣的:(leetcode,链表,算法)