题目信息
LeetCode hot 100 --- 138. 随机链表的复制
给你一个长度为
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 <= 104Node.random为null或指向链表中的节点。
思考过程和自写代码
简单来说就是一个深拷贝构造的情景。
node加入一个random指针随机指向null节点或者是链表中的任意一个节点。
我的思考逻辑是要走两遍原链表的循环,先按照next顺序构造出链表,然后通过random的映射进行random指针的完善。
第一版本代码
python
"""
# Definition for a Node.
class Node:
def __init__(self, x: int, next: 'Node' = None, random: 'Node' = None):
self.val = int(x)
self.next = next
self.random = random
"""
class Solution:
def copyRandomList(self, head: 'Optional[Node]') -> 'Optional[Node]':
# 进行两轮?先进行next的,也就是完全链表的构造(避免random指向的节点尚未创建)
if head is None: return None
random_map = {} # 每个位置的random指向的地址 index.random = 具体的地址
orin_map = {} # 地址->index 每个地址对应的Index
now_map = {} # 深copy后的index到地址
random_map[1] = head.random
orin_map[head] = 1
'''
第一遍遍历的时候, 记录下random_map and orin_map;
然后第二遍遍历deep copy的链表的时候:
- random_map[i] = 原链表random指向的地址
- orin_map[random_map[i]] -----> 拿到对应的index序号
- curr.next = now_map[index] = now_map[orin_map[random_map[i]]]
'''
res = Node(head.val, None, None)
r = res
now_map[1] = res
pre, curr = head, head.next
cnt = 1
while curr is not None:
tmp = Node(curr.val, None, None)
r.next = tmp
cnt += 1
random_map[cnt] = curr.random
orin_map[curr] = cnt
now_map[cnt] = tmp
pre = curr
curr = curr.next
r = r.next
# 完成链表的创建, 开始random的创建
# 重新初始化变量的位置
pre, curr = head, head.next
r = res
for i in range(1, cnt + 1):
address = random_map[i] # 拿到对应的地址
index = orin_map[address]
deep_copy_address = now_map[index]
r.random = deep_copy_address
r = r.next
return res
改版本代码编译不通过,仅代表初步的思路和大体的框架
逻辑 / 代码优化
大体的方向 / 逻辑是正确的,有几个关键的问题 / 需要优化的点
**最大的问题是:没有考虑到random为None的情况。**如果为none,index = orin_mapaddress 语句会产生报错。
需要优化的几个点:
- 数据映射过于弯绕。数据映射现有的逻辑是 源节点random地址------>源节点序号------>新节点地址,逻辑上没有问题,但没有必要通过中间的序号做桥接。可以直接进行源节点对象------>新节点对象的映射,更为直接。
- 部分变量没有使用。比如在第二轮的遍历过程当中,pre and curr都没有被使用到
简化后的代码简洁非常多。
创建新节点;建立旧新节点的映射关系;建立指针关系
第二版代码-终版
python
"""
# Definition for a Node.
class Node:
def __init__(self, x: int, next: 'Node' = None, random: 'Node' = None):
self.val = int(x)
self.next = next
self.random = random
"""
class Solution:
def copyRandomList(self, head: 'Optional[Node]') -> 'Optional[Node]':
if head is None: return None
copy_map = {None: None} #建立新旧节点之间的映射关系
curr = head
while curr is not None:
copy_map[curr] = Node(curr.val) # 旧节点映射新节点
curr = curr.next
curr = head
# 开始建立next and random关系
while curr is not None:
new_node = copy_map[curr]
new_node.next = copy_map[curr.next]
new_node.random = copy_map[curr.random]
curr = curr.next
return copy_map[head]
简洁很多了,逻辑也非常的清楚


