题目描述:
请实现 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 = [[3,null],[3,0],[3,null]]
输出:[[3,null],[3,0],[3,null]]
示例 4:
输入:head = []
输出:[]
解释:给定的链表为空(空指针),因此返回 null。
提示:
- − 10000 < = N o d e . v a l < = 10000 -10000 <= Node.val <= 10000 −10000<=Node.val<=10000
- Node.random 为空(null)或指向链表中的节点
- 节点数目不超过 1000
复制单纯的单链表很容易,根据题目描述主要需要考虑每个结点的随机指针,即每个结点random的指向。
-
暴力求解:先遍历列表同时记录val和random域的值,分别存放在
val_list和random_list中,然后,首先使用val_list中的元素依次新建结点并设置next构成基本链。构建两个指针p和r,其中p指向当前结点,r用于搜索random指向的结点。接着从新链第一个节点newHead.next开始,看它在random_list中是否有值,如果有,从头遍历链表,直到找到val为指定值的结点,将当前结点的random指向它,同时将p指向下一个位置,r回到头结点。不断遍历,直到p.next == None为止。但这样的算法复杂度为O(n^2),在OJ平台上会发生时间超出的错误。 -
聪明的求法:先依次复制给定链表的结点构成新链,然后再断链得到原始链表的复制,具体的过程如下所示:
假设初始链表如上图所示,链表中包含1、2、3、4四个结点,结点的random指向分别为:
1.random = 3
2.random = 2
3.random = 4
4.random = 2
首先,复制每个结点将其链接在它后面构成如(2)所示的新链:
cur = head
# 始终保证cur指向原始链表中的结点
while cur:
node = Node(cur.label)
# 在原始链中插入
node.next = cur.next
cur.next = node
cur = node.next
得到插入复制结点后的新链表后,我们还需要设置新结点的random,使其和它和给定链表中的结点一致:
cur = head
while cur:
# 可能某些结点的random为None,因此先进行判断
if cur.random:
cur.next.random = cur.random.next
# 始终保证cur指向原始链表中的结点
cur = cur.next.next
经过上述步骤,新链表的中每个结点的next和random都指向了正确的结点,但这样的链表不是我们想要的。由于不能直接返回给定链表的头结点,我们需要从新链表中将复制的节点抽出来,同时保证抽出的结点的next和random指向和最开始的链表一致。
cur = head
# 指向复制结点中的第一个结点,只用于输出
h = head.next
# 指向复制结点中的第一个结点,用于断链
p = head.next
while cur:
# 断开原链表中的结点和复制结点的next指向,将next指向原链表中的对应结点,即next.next
cur.next = cur.next.next
# 看是否遍历完了所有的结点
if p.next:
# 类似上一步
p.next = p.next.next
# 由于此时new_cur指向的结点的next已经发生了改变,因此只需要将next后移即可
p= p.next
# 类似new_cur的上一步操作
cur = cur.next
最终newHead作为头结点的链表就是复制后的链表,即我们想要的结果。由于在复制完结点后所做的只是结点next和random指向的操作,因此时间复杂度为 O ( n ) O(n) O(n)。
"""
# 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: 'Node') -> 'Node':
if not head: return None
cur = head
# next
while cur:
node = Node(cur.val)
node.next= cur.next
cur.next = node
cur = cur.next.next
# random
cur = head
while cur:
if cur.random:
cur.next.random = cur.random.next
cur = cur.next.next
#
cur = head
h = head.next
p = head.next
while cur:
cur.next = cur.next.next
if p.next:
p.next = p.next.next
p = p.next
cur = cur.next
return h