You are given the head of a linked list where each node contains a value and a random pointer. The random pointer can point to any node in the list, or it can be null. Your task is to create a deep copy of this linked list. The deep copy should consist of entirely new nodes, and the next and random pointers of the new nodes should mirror the structure of the original list, ensuring that no pointers in the new list point back to the original list's nodes.
For example, consider a linked list with three nodes:
Node 1: value = 7, random = null Node 2: value = 13, random = Node 1 Node 3: value = 11, random = Node 3
Your deep copy should create three new nodes with the same values, and their random pointers should be set up such that:
New Node 1: value = 7, random = null New Node 2: value = 13, random = New Node 1 New Node 3: value = 11, random = New Node 3
Describe an algorithm to implement this deep copy. What is the time and space complexity of your approach? Can you optimize it for space complexity?
Given a linked list where each node has a next
pointer and a random
pointer (which can point to any node or be null), create a deep copy of the list.
next
Pointers: Iterate through the original list again. For each node, find its corresponding new node in the hash map, and set the next
pointer of the new node to the new node corresponding to the original node's next
pointer (if it exists).random
Pointers: Iterate through the original list one more time. For each node, find its corresponding new node in the hash map, and set the random
pointer of the new node to the new node corresponding to the original node's random
pointer (if it exists).class Node:
def __init__(self, val, next=None, random=None):
self.val = val
self.next = next
self.random = random
def copyRandomList_naive(head: 'Node') -> 'Node':
if not head:
return None
# 1. Create new nodes and store the mapping
old_to_new = {}
curr = head
while curr:
new_node = Node(curr.val)
old_to_new[curr] = new_node
curr = curr.next
# 2. Set next pointers
curr = head
while curr:
new_node = old_to_new[curr]
if curr.next:
new_node.next = old_to_new[curr.next]
curr = curr.next
# 3. Set random pointers
curr = head
while curr:
new_node = old_to_new[curr]
if curr.random:
new_node.random = old_to_new[curr.random]
curr = curr.next
return old_to_new[head]
The optimal solution reduces the space complexity to O(1) by weaving the new nodes into the original list. After creating the new nodes, interweave the original and copied list, and then adjust the random pointers.
random
Pointers: Iterate through the interweaved list. For each new node, its random
pointer should point to the copy of the node its original counterpart's random pointer points to. E.g., A'.random = A.random.next (if A.random exists).next
pointers of both lists to restore the original list and form the new copied list.def copyRandomList(head: 'Node') -> 'Node':
if not head:
return None
# 1. Interweave new nodes
curr = head
while curr:
new_node = Node(curr.val)
new_node.next = curr.next
curr.next = new_node
curr = new_node.next
# 2. Set random pointers
curr = head
while curr:
if curr.random:
curr.next.random = curr.random.next
curr = curr.next.next
# 3. Separate lists
curr = head
new_head = head.next
new_curr = new_head
while curr:
curr.next = new_curr.next
curr = curr.next
if curr:
new_curr.next = curr.next
new_curr = new_curr.next
else:
new_curr.next = None
return new_head
head
is None), return None.random
Pointer is None: The algorithm should handle cases where the random
pointer of a node is None.