题目:
例如
面试tips:
询问有无时间复杂度或空间复杂度的限制。
思路:
本题的本质就是复杂链表的深拷贝
1. 暴力解法 → 第一次遍历原链表时构建一个复制了next的新链表,第二次遍历原链表,对每个原链表的节点的random从头寻找,同时同步在新链表寻找,即可找到复制链表每个节点的random。
时复O(N^2), 空复O(1)
2. 用空间换取时间,利用哈希表。在第一次遍历原链表时存储(原链表节点,新链表节点)的映射关系;则在第二次遍历原链表时为新链表根据哈希映射找到其next和random。
时复O(N), 空复O(N)
3. 拼接+拆分;时复O(N),空复O(1)。
第一步:遍历原链表时对其进行复制,并将原链表(A,B,C,D...)与新链表(A',B',C',D',...)拼接成如下形式。
第二步:遍历上述链表的原节点(curr = curr.next.next),只要curr.random不为空,则curr.next(新链表对应节点)的random即为curr.random.next。说人话就是:上图中第一步构建完后就是上图,可以看到新链表节点(A',B',C',D',...)是没有random指针的(random指针为None), 这时可通过其前一个节点帮助找其复制后的random节点,例如
第三步:将其交错拆分,拆分时用next指针连接即可。
代码实现:
因为是acm模式,因此包括了二维数组转复杂链表,复杂链表转二维数组的过程,因此较长。核心代码模式从class solution开始,但很多大厂面试就是要自己从数组转链表,再从链表转数组进行打印,因此这里记录acm模式。
1. 思路一实现
class ListNode:
def __init__(self, val , next = None, random = None):
self.val = val
self.next = next
self.random = random
def arr2List(arr_2):
# 二维数组构建复杂链表函数
# 构建时将其存入哈希表
my_dict = {}
dummy_head = prev = ListNode(0)
for i in range(len(arr_2)):
my_dict[i] = prev.next = ListNode(arr_2[i][0])
prev = prev.next
# 至此dummy_head即为构建好的带有next的链表,但是没有random指针
# 二维数组的第二个数字表示该节点random指针所指向的第几个节点
prev = dummy_head.next
for i in range(len(arr_2)):
if arr_2[i][1] != None:
prev.random = my_dict[arr_2[i][1]]
else:
prev.random = None
prev = prev.next
return dummy_head.next
def List2arr(head):
# 复杂链表转二维数组
arr_2 = []
my_dict = {}
curr = head
i = 0
while curr:
tmp = [curr.val]
arr_2.append(tmp)
my_dict[curr] = i
i += 1
curr = curr.next
curr = head
while curr:
# 添加random
arr_2[my_dict[curr]].append(my_dict[curr.random] if curr.random else None)
curr = curr.next
return arr_2
class Solution:
def copyRandomList(self, head):
# 暴力方法 -- 先构建只含有next的单链表,再构建random的
curr = head
new_head = prev = ListNode(0) # 虚拟指针 使得头节点操作与其他同
while curr:
new_curr = ListNode(curr.val)
prev.next = new_curr
prev, curr = new_curr, curr.next
# 至此 复制了链表的第一层(即带next指针的)
# 接下来 需要给复制链表上的每个节点添加random指针 原链表上从头开始走了多少步则复制链表上也从头开始走了多少步
curr1 = head
new_curr1 = new_head.next
while curr1:
curr2 = head
new_curr2 = new_head.next
# 找到curr1所对应的random,同步找到new_curr1所对应的random
while curr2 and curr1.random != curr2:
curr2 = curr2.next
new_curr2 = new_curr2.next
# 跳出来 此时即 curr2 == curr1.random,那么new_curr2所指向的位置即为new_curr1的random
new_curr1.random = new_curr2
curr1 = curr1.next
new_curr1 = new_curr1.next
return new_head.next
if __name__ == '__main__':
# 复杂链表的构建,二维数组构建复杂链表,(用单链表用一维数组、双链表用二维数组构建的思想)
arr_2 = [[7, None],[13, 0],[11, 4],[10, 2],[1, 0]]
a = Solution()
# 先将二维数组转为链表
head = arr2List(arr_2)
new_head = a.copyRandomList(head)
# 再将链表转为二维数组
new_arr2 = List2arr(new_head)
print(new_arr2)
2. 思路二实现
class ListNode:
def __init__(self, val , next = None, random = None):
self.val = val
self.next = next
self.random = random
def arr2List(arr_2):
# 二维数组构建复杂链表函数
# 构建时将其存入哈希表
my_dict = {}
dummy_head = prev = ListNode(0)
for i in range(len(arr_2)):
my_dict[i] = prev.next = ListNode(arr_2[i][0])
prev = prev.next
# 至此dummy_head即为构建好的带有next的链表,但是没有random指针
# 二维数组的第二个数字表示该节点random指针所指向的第几个节点
prev = dummy_head.next
for i in range(len(arr_2)):
if arr_2[i][1] != None:
prev.random = my_dict[arr_2[i][1]]
else:
prev.random = None
prev = prev.next
return dummy_head.next
def List2arr(head):
# 复杂链表转二维数组
arr_2 = []
my_dict = {}
curr = head
i = 0
while curr:
tmp = [curr.val]
arr_2.append(tmp)
my_dict[curr] = i
i += 1
curr = curr.next
curr = head
while curr:
# 添加random
arr_2[my_dict[curr]].append(my_dict[curr.random] if curr.random else None)
curr = curr.next
return arr_2
class Solution:
def copyRandomList(self, head) :
# 前一种方法时复为O(n^2),主要是在寻找random指针时用时太久
# 用空间换时间,在一次构造遍历时就存储好每个节点random指针所对应的复制节点
# 建立哈希映射
if not head:
return None
my_dict = {}
curr = head
while curr:
my_dict[curr] = ListNode(curr.val)
curr = curr.next
# 对复制的链表进行next和random指针赋值
curr = head
while curr:
# 这里不用my_dict[curr].next = my_dict[curr.next]
# my_dict[curr].random = my_dict[curr.random]
# 的原因是当curr.random为空时 是没有被存储下来 因此用get 若取不到则为None
my_dict[curr].next = my_dict.get(curr.next)
my_dict[curr].random = my_dict.get(curr.random)
curr = curr.next
return my_dict[head]
if __name__ == '__main__':
# 复杂链表的构建,二维数组构建复杂链表,(用单链表用一维数组、双链表用二维数组构建的思想)
arr_2 = [[7, None],[13, 0],[11, 4],[10, 2],[1, 0]]
a = Solution()
# 先将二维数组转为链表
head = arr2List(arr_2)
new_head = a.copyRandomList(head)
# 再将链表转为二维数组
new_arr2 = List2arr(new_head)
print(new_arr2)
3. 思路三实现
class ListNode:
def __init__(self, val , next = None, random = None):
self.val = val
self.next = next
self.random = random
def arr2List(arr_2):
# 二维数组构建复杂链表函数
# 构建时将其存入哈希表
my_dict = {}
dummy_head = prev = ListNode(0)
for i in range(len(arr_2)):
my_dict[i] = prev.next = ListNode(arr_2[i][0])
prev = prev.next
# 至此dummy_head即为构建好的带有next的链表,但是没有random指针
# 二维数组的第二个数字表示该节点random指针所指向的第几个节点
prev = dummy_head.next
for i in range(len(arr_2)):
if arr_2[i][1] != None:
prev.random = my_dict[arr_2[i][1]]
else:
prev.random = None
prev = prev.next
return dummy_head.next
def List2arr(head):
# 复杂链表转二维数组
arr_2 = []
my_dict = {}
curr = head
i = 0
while curr:
tmp = [curr.val]
arr_2.append(tmp)
my_dict[curr] = i
i += 1
curr = curr.next
curr = head
while curr:
# 添加random
arr_2[my_dict[curr]].append(my_dict[curr.random] if curr.random else None)
curr = curr.next
return arr_2
class Solution:
def copyRandomList(self, head):
# 在不使用额外空间的情况下 时复为O(n).
if not head:
# 这里提前判断是为第三步时要赋予两个头节点
return None
# 1先将复制链表与原链表串联
curr = head
while curr:
new_curr = ListNode(curr.val)
new_curr.next = curr.next
curr.next = new_curr
curr = new_curr.next
# 这样就将复制链表与原链表形成一个单链表,奇数节点为单链表(有random),偶数节点为复制链表(random未赋值)
# 2为复制链表添加random(复制链表的节点特征为random为空)从不为空的random节点提取出random的next给next
curr = head
while curr:
if curr.random:
# 只要curr.random不为空,则其next的random与其random复制的节点同,即random.next
curr.next.random = curr.random.next
curr = curr.next.next # 这里不curr=curr.next的原因是在执行时curr.next可能会空
# 至此,就已连接好random,下面进行拆分并链接next
# 3.现进行拆分并连接,构成next
curr = head
new_prev = new_curr = head.next
while new_curr.next:
curr.next = new_curr.next
new_curr.next = new_curr.next.next
curr, new_curr = curr.next, new_curr.next
# 还原原链表结构(单独处理其尾节点)
curr.next = None
return new_prev
if __name__ == '__main__':
# 复杂链表的构建,二维数组构建复杂链表,(用单链表用一维数组、双链表用二维数组构建的思想)
arr_2 = [[7, None],[13, 0],[11, 4],[10, 2],[1, 0]]
a = Solution()
# 先将二维数组转为链表
head = arr2List(arr_2)
new_head = a.copyRandomList(head)
# 再将链表转为二维数组
new_arr2 = List2arr(new_head)
print(new_arr2)
参考资料:
1. 《剑指offer》
2. 力扣(LeetCode)官网 - 全球极客挚爱的技术成长平台