day6-2022.10.30
题目信息来源
作者:Krahets
链接:https://leetcode.cn/leetbook/read/illustration-of-algorithm
来源:力扣(LeetCode)
剑指 Offer 35. 复杂链表的复制
请实现 copyRandomList 函数,复制一个复杂链表。在复杂链表中,每个节点除了有一个 next 指针指向下一个节点,还有一个 random 指针指向链表中的任意节点或者 null。
输入:head = [[7,null],[13,0],[11,4],[10,2],[1,0]]
输出:[[7,null],[13,0],[11,4],[10,2],[1,0]]
输入:head = []
输出:[]
解释:给定的链表为空(空指针),因此返回 null。
题解一:哈希表
- 和普通的链表复制不同,还多了一个random,这意味着在第一轮遍历时不能像普通的链表复制一样,直接复制,因为copy_node的randow可能还没有创建。因为这个必须需要两轮遍历。第一轮遍历,从头到尾创建所有copy_node,第二轮遍历指向next和random
- 第二个问题来了,如果我们在第一轮遍历时,保存了原链表和copy链表的头节点,那后续我们可以获得next和copy_next。但怎么获得copy_random呢?或者说,我们怎么根据一个node获得第一轮遍历时创建的相对应的copy_node呢?解决方案时用字典,或者是哈希,创建node-copy_node对,来存储对应关系。这样我们只需要原链表的head就可以陆续获得所有的节点。
知识点
dict.get(key[,value])
:返回指定键的值,如果指定键的值不存在,返回value默认值(默认值可以指定,没有指定就是None),相比较而言dict[key]
会在key
不存在时抛出keyerror
的错误,get
不会。但是get
的速度要慢一点。这个题里用 get 好一点- 代码的简洁性,主要体现在这几句,本来我写了有7行,其实只需要两行
mydict[head].next = mydict.get(head.next) # copy_head的next为copy_next
mydict[head].random = mydict.get(head.random)
"""
# 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
else:
mydict = {} # 保存节点和对应的复制节点
head_node = head # 保存头节点
# 第一轮遍历,只是把节点和节点的next创建一下,只所以选择分两轮,是因为第一轮时copy的next可能还未创建
while head:
mydict[head] = Node(head.val) # 保存head copy_head键值对
head = head.next # 将当前head变为head.next
head = head_node
# 第二轮遍历,发现不是很简单,因为next是Node类型,第二轮遍历,不管是copy还有原来的都不好找
# 查看了解析,似乎使用字典存储head和对应的copy_head可以方便查找
while head:
mydict[head].next = mydict.get(head.next) # copy_head的next为copy_next
mydict[head].random = mydict.get(head.random)
head = head.next
return mydict[head_node]
题解二:拼接+拆分
要注意while后面跟的值
"""
# 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
now = head # 获得head
# 第一轮遍历,插入
while now:
node = Node(now.val) # 插入now的复制节点
node.next = now.next # 将now的复制节点的next复制为now的next,next必须指定使链表连接起来
now.next = node # now.next变成复制节点,random暂且不赋值
now = node.next
# 第二轮遍历,指向randow,这个时候还不能拆,因为有可能randow指向链表的前面部分节点
now = head # 获得head
while now:
node = now.next # 获得插入的节点
if now.random:
node.random = now.random.next# randow为now的random后面的插入节点
now = node.next
# 第三轮遍历,拆
now = head
node = copy_head = head.next
# 这里要用node.next来确定是否有下一个节点,now.next没拆之前是一定存在的
while node.next:
now.next = node.next
now = now.next
node.next = now.next
node = node.next
return copy_head