跳到正文
第 138 题中等

随机链表的复制

Copy List with Random Pointer

复制一条带 random 指针的链表,新链表的 next 和 random 都要指向新节点,不能指向原节点。

在力扣中练习

题目描述

给你一个长度为 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 作为传入参数。

 

示例 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 = [[3,null],[3,0],[3,null]]
输出:[[3,null],[3,0],[3,null]]

 

提示:

  • 0 <= n <= 1000
  • -104 <= Node.val <= 104
  • Node.random 为 null 或指向链表中的节点。

 

解题思路

主解法

哈希表 · 两遍复制

第一遍只建节点,第二遍用映射接两种边

复制值很容易,难的是 random:它可能指向还没创建的节点,也可能指回前面。直接边遍历边复制,很容易拿到原链表的指针。

把工作拆成两遍。第一遍只创建新节点,并建立 原节点 → 新节点 的映射;此时新节点之间还没有边。

图解
原链表 · 实线 next,虚线 random
7O0
13O1
11O2
10O3
复制链表 · 值相同,节点全新
7C0
13C1
11C2
10C3
普通节点
先创建所有新节点,再统一接 next,避免边接边找导致空指针。

第二遍再遍历原链表,用映射接边:next 和 random 都先查表,目标为空时显式写 null。这样无论 random 指向前面、后面还是 null,拿到的都是新节点。

分步图解

再补 random:C0.random = C2;不能直接复用旧指针。

cur
O0
原链表 · 实线 next,虚线 random
7O0
13O1
11O2
10O3
复制链表 · 值相同,节点全新
7C0
13C1
11C2
10C3
1 / 4
random 必须经过 map 映射到新节点,不能直接复制旧节点的 random 地址。

自拟主例的 random 同时包含前向(7 → 11)、后向(13 → 7)、相邻(11 → 10)和 null 四种情况;如果漏掉空值判断,map[nullptr] 会插入一个多余条目,甚至解引用空指针。

时间 O(n),空间 O(n) 用于映射。

时间 O(n)空间 O(n)

易错点

  • 边遍历边复制 random:目标节点可能尚未创建,拿到的是旧指针。
  • 不判断 cur->next / cur->random 是否为空,直接 map[...] 会插入空键。
  • 复制后仍返回原 head:新链表没被返回,等于没复制。

代码与演示

演示输入head = [7,13,11,10];random 分别指向 11、7、10、null

原链表 4 个节点,next 依次连接;random 可能向前、向后或指向 null。

原链表 · 实线 next,虚线 random
7O0
13O1
11O2
10O3
复制链表 · 值相同,节点全新
1 / 5
查看 C++ 代码

延伸阅读

  • “先建节点、再连边”是复制图结构的通用套路,图和随机链表都适用。
  • 如果要求 O(1) 额外空间,可以把新节点插到原节点后面,利用旧链的 next 当映射,最后再拆开;代价是代码更绕,而且会临时修改原链表。
  • 判断复制是否成功,不能只看值:必须确认 next/random 指向的是新节点地址。