You are given the head of a linked list of length n. Unlike a singly linked list, each node contains an additional pointer random, which may point to any node in the list, or null.
Create a deep copy of the list.
The deep copy should consist of exactly n new nodes, each including:
val of the copied nodenext pointer to the new node corresponding to the next pointer of the original noderandom pointer to the new node corresponding to the random pointer of the original nodeNote: None of the pointers in the new list should point to nodes in the original list.
Return the head of the copied linked list.
In the examples, the linked list is represented as a list of n nodes. Each node is represented as a pair of [val, random_index] where random_index is the index of the node (0-indexed) that the random pointer points to, or null if it does not point to any node.
Example 1:
Input: head = [[3,null],[7,3],[4,0],[5,1]]
Output: [[3,null],[7,3],[4,0],[5,1]]Example 2:
Input: head = [[1,null],[2,2],[3,2]]
Output: [[1,null],[2,2],[3,2]]Constraints:
0 <= n <= 100-100 <= Node.val <= 100random is null or is pointing to some node in the linked list.
You should aim for a solution as good or better than O(n) time and O(n) space, where n is the length of the given list.
There is an extra random pointer for each node, and unlike the next pointer, which points to the next node, the random pointer can point to any random node in the list. A deep copy is meant to create completely separate nodes occupying different memory. Why can't we build a new list while iterating through the original list?
Because, while iterating through the list, when we encounter a node and create a copy of it, we can't immediately assign the random pointer's address. This is because the random pointer might point to a node that has not yet been created. To solve this, we can first create copies of all the nodes in one iteration. However, we still can't directly assign the random pointers since we don't have the addresses of the copies of those random pointers. Can you think of a data structure to store this information? Maybe a hash data structure could help.
We can use a hash data structure, such as a hash map, which takes O(1) time to retrieve data. This can help by mapping the original nodes to their corresponding copies. This way, we can easily retrieve the copy of any node and assign the random pointers in a subsequent pass after creating copies of all nodes in the first pass.
Before attempting this problem, you should be comfortable with:
We must create a deep copy of a linked list where each node has both next and random pointers.
The main difficulty: multiple nodes may point to the same random node, so we must ensure each original node is copied exactly once.
A hash map helps us remember the copied version of each original node.
Using recursion, we:
next,random using the map.null, return null.next pointer.random pointer using the map."""
# 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 __init__(self):
self.map = {}
def copyRandomList(self, head: 'Optional[Node]') -> 'Optional[Node]':
if head is None:
return None
if head in self.map:
return self.map[head]
copy = Node(head.val)
self.map[head] = copy
copy.next = self.copyRandomList(head.next)
copy.random = self.map.get(head.random)
return copyWe want to copy a linked list where each node has both next and random pointers.
The challenge is that the random pointer can point anywhere — forward, backward, or even None.
So we must ensure every original node is copied exactly once, and all pointers are reconnected correctly.
A simple solution:
original_node → copied_nodenext and random pointers for each copied node.This guarantees all pointers are valid and no node is duplicated.
oldToCopy, mapping each original node to its copied node.null -> null for convenience.oldToCopy.copy.next using oldToCopy[original.next].copy.random using oldToCopy[original.random].oldToCopy[head]."""
# 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]':
oldToCopy = {None: None}
cur = head
while cur:
copy = Node(cur.val)
oldToCopy[cur] = copy
cur = cur.next
cur = head
while cur:
copy = oldToCopy[cur]
copy.next = oldToCopy[cur.next]
copy.random = oldToCopy[cur.random]
cur = cur.next
return oldToCopy[head]We want to copy a linked list where every node has a next pointer and a random pointer.
Normally we need two passes: one to create nodes, and another to connect pointers.
But we can actually do both at the same time by using a hash map that automatically creates a copy node whenever we access it.
So when we say:
oldToCopy[cur] → gives the copied node oldToCopy[cur.next] → gives the copy of cur.next (created if not present) oldToCopy[cur.random] → gives the copy of cur.randomThis lets us fill val, next, and random in one single pass.
oldToCopy that returns a new empty node whenever we access a key that doesn't exist yet.oldToCopy[null] = null so next or random can safely point to null.next pointer using the map.random pointer using the map."""
# 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]':
oldToCopy = collections.defaultdict(lambda: Node(0))
oldToCopy[None] = None
cur = head
while cur:
oldToCopy[cur].val = cur.val
oldToCopy[cur].next = oldToCopy[cur.next]
oldToCopy[cur].random = oldToCopy[cur.random]
cur = cur.next
return oldToCopy[head]We want to copy the list without using extra space like a hash map.
The trick is to interleave copied nodes inside the original list:
Original:
A → B → C
After interleaving:
A → A' → B → B' → C → C'
Now every copied node (A', B', C') is right next to the original node, so we can easily:
random of the copied node: A'.random = A.random.nextThis gives a perfect clone using O(1) extra space.
Interleave copy nodes
A, create a copy A' and insert it right after A.Assign random pointers
A, if A.random exists, then set A'.random = A.random.next.Unweave the lists
Return the head of the copied list.
"""
# 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
l1 = head
while l1 is not None:
l2 = Node(l1.val)
l2.next = l1.next
l1.next = l2
l1 = l2.next
newHead = head.next
l1 = head
while l1 is not None:
if l1.random is not None:
l1.next.random = l1.random.next
l1 = l1.next.next
l1 = head
while l1 is not None:
l2 = l1.next
l1.next = l2.next
if l2.next is not None:
l2.next = l2.next.next
l1 = l1.next
return newHeadThis method also avoids extra space like a hash map, but instead of inserting copied nodes into the next pointer chain, we temporarily use the random pointer to store the copied nodes.
For every original node:
random pointer.next pointer initially points to whatever the original node’s random was pointing to.This allows us to:
Everything happens using only pointer manipulations — no extra arrays, no hash map.
Create all copied nodes
A, create a copy A'.A.random.A'.next = A.random_old.Fix random pointers of copies
A, set A'.random = A.random_old.random' (if exists).Extract the copied list and restore original list
A:A.random = original_random.A'.next to the next copied node.Return the head of the copied list (which is 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: 'Optional[Node]') -> 'Optional[Node]':
if head is None:
return None
l1 = head
while l1:
l2 = Node(l1.val)
l2.next = l1.random
l1.random = l2
l1 = l1.next
newHead = head.random
l1 = head
while l1:
l2 = l1.random
l2.random = l2.next.random if l2.next else None
l1 = l1.next
l1 = head
while l1 is not None:
l2 = l1.random
l1.random = l2.next
l2.next = l1.next.random if l1.next else None
l1 = l1.next
return newHeadWithout a hash map to track already-copied nodes, you might create duplicate copies when multiple random pointers point to the same node. Always check if a node has been copied before creating a new copy.
# Wrong - creates duplicates
copy.random = Node(original.random.val)
# Correct - use hash map to reuse existing copy
copy.random = oldToCopy[original.random]The random pointer can be null. Attempting to access properties of null causes crashes. Always check for null before dereferencing.
# Wrong - crashes if random is None
copy.random = oldToCopy[original.random] # KeyError if random is None
# Correct - handle None explicitly
oldToCopy[None] = None # or check before accessIn the interleaving approach, failing to properly unweave the two lists corrupts the original list and may break the copied list's pointers. The separation step must correctly restore both next pointers.
# During separation, update both lists
l1.next = l2.next # restore original
l2.next = l2.next.next # link copies (check for null first)