剑指 Offer 35. 复杂链表的复制

本文介绍了如何复制一个包含random指针的复杂链表,提供了两种解题思路:一是使用哈希表辅助复制,二是通过拼接链表后再拆分的方法。详细阐述了每种方法的实现步骤和逻辑,帮助读者理解复杂链表复制的难点和解决方案。
摘要由CSDN通过智能技术生成

请实现 copyRandomList 函数,复制一个复杂链表。在复杂链表中,每个节点除了有一个 next 指针指向下一个节点,还有一个 random 指针指向链表中的任意节点或者 null

示例 1:

输入:head = [[7,null],[13,0],[11,4],[10,2],[1,0]]
输出:[[7,null],[13,0],[11,4],[10,2],[1,0]]

示例 2:
在这里插入图片描述

输入:head = [[1,1],[2,1]]
输出:[[1,1],[2,1]]

示例 3:

输入:head = []
输出:[]
解释:给定的链表为空(空指针),因此返回 null。

老废物做题失败,看一看大佬的解法

解题思路:

普通链表的节点定义如下:

class Node:
    def __init__(self, x: int, next: 'Node' = None):
        self.val = int(x)
        self.next = next

本题链表的节点定义如下:

class Node:
    def __init__(self, x: int, next: 'Node' = None, random: 'Node' = None):
        self.val = int(x)
        self.next = next
        self.random = random

给定链表的头节点 head ,复制普通链表很简单,只需遍历链表,每轮建立新节点 + 构建前驱节点 pre 和当前节点 node 的引用指向即可。

本题链表的节点新增了 random 指针,指向链表中的 任意节点 或者 n u l l null null 。这个 random 指针意味着在复制过程中,除了构建前驱节点和当前节点的引用指向 pre.next ,还要构建前驱节点和其随机节点的引用指向 pre.random

本题难点: 在复制链表的过程中构建新链表各节点的 random 引用指向。

在这里插入图片描述

class Solution:
    def copyRandomList(self, head: 'Node') -> 'Node':
        cur = head
        dum = pre = Node(0)
        while cur:
            node = Node(cur.val) # 复制节点 cur
            pre.next = node      # 新链表的 前驱节点 -> 当前节点
            # pre.random = '???' # 新链表的 「 前驱节点 -> 当前节点 」 无法确定
            cur = cur.next       # 遍历下一节点
            pre = node           # 保存当前新节点
        return dum.next

方法一:哈希表

利用哈希表的查询特点,考虑构建 原链表节点新链表对应节点 的键值对映射关系,再遍历构建新链表各节点的 nextrandom 引用指向即可。

算法流程:

  1. 若头节点 head 为空节点,直接返回 n u l l null null
  2. 初始化: 哈希表 dic , 节点 cur 指向头节点;
  3. 复制链表:
    1. 建立新节点,并向 dic 添加键值对 (原 cur 节点, 新 cur 节点);
    2. cur 遍历至原链表下一节点;
  4. 构建新链表的引用指向:
    1. 构建新节点的 nextrandom 引用指向;
    2. cur 遍历至原链表下一节点;
  5. 返回值: 新链表的头节点 dic[cur]
class Solution:
    def copyRandomList(self, head: 'Node') -> 'Node':
        dic = {}
        cur = head

        while cur:
            dic[cur] = Node(cur.val)
            cur = cur.next
 
        cur = head
        while cur:
            dic[cur].next = dic.get(cur.next)
            dic[cur].random = dic.get(cur.random)
            cur = cur.next

        cur = head

        return dic.get(cur)

方法二:拼接 + 拆分

考虑构建 原节点 1 -> 新节点 1 -> 原节点 2 -> 新节点 2 -> …… 的拼接链表,如此便可在访问原节点的 random 指向节点的同时找到新对应新节点的 random 指向节点。

算法流程:

  1. 复制各节点,构建拼接链表:
    设原链表为 n o d e 1 → n o d e 2 → ⋯ node1 \rightarrow node2 \rightarrow \cdots node1node2,构建的拼接链表如下所示: n o d e 1 → n o d e 1 n e w → n o d e 2 → n o d e 2 n e w → ⋯ node1 \rightarrow node1_{new} \rightarrow node2 \rightarrow node2_{new} \rightarrow \cdots node1node1newnode2node2new
  2. 构建新链表各节点的 random 指向:
    当访问原节点 cur 的随机指向节点 cur.random 时,对应新节点 cur.next 的随机指向节点为 cur.random.next
  3. 拆分原 / 新链表:
    设置 pre / cur 分别指向原 / 新链表头节点,遍历执行 pre.next = pre.next.nextcur.next = cur.next.next 将两链表拆分开。
  4. 返回新链表的头节点 res 即可。
class Solution:
   def copyRandomList(self, head: 'Node') -> 'Node':
        if not head: return
        cur = head
        # 1. 复制各节点,并构建拼接链表
        while cur:
            tmp = Node(cur.val)
            tmp.next = cur.next
            cur.next = tmp
            cur = tmp.next
        # 2. 构建各新节点的 random 指向
        cur = head
        while cur:
            if cur.random:
                cur.next.random = cur.random.next
            cur = cur.next.next
        # 3. 拆分两链表
        cur = res = head.next
        pre = head
        while cur.next:
            pre.next = pre.next.next
            cur.next = cur.next.next
            pre = pre.next
            cur = cur.next
        pre.next = None # 单独处理原链表尾节点
        return res      # 返回新链表头节点

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值