138. Copy List With Random Pointer - Explanation
Description
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:
- The original value
valof the copied node - A
nextpointer to the new node corresponding to thenextpointer of the original node - A
randompointer to the new node corresponding to therandompointer of the original node
Note: 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 <= 100- Node values are not guaranteed to be unique.
randomisnullor is pointing to some node in the linked list.
Topics
Recommended Time & Space Complexity
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.
Hint 1
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?
Hint 2
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.
Hint 3
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.
Prerequisites
Before attempting this problem, you should be comfortable with:
- Linked Lists - Understanding node structures with next pointers and traversal techniques
- Hash Maps - Using dictionaries to map original nodes to their copies for O(1) lookup
- Recursion - Building deep copies by recursively processing next and random pointers
- Pointer Manipulation - Modifying node pointers to interleave or rearrange list structure
1. Recursion + Hash Map
Intuition
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:
- copy the current node,
- store it in the map,
- recursively copy its
next, - link its
randomusing the map.
Algorithm
- If the current node is
null, returnnull. - If the node is already copied (exists in the map), return the stored copy.
- Create a new copy node and store it in the map.
- Recursively copy the
nextpointer. - Set the
randompointer using the map. - Return the copied node.
"""
# 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 copyTime & Space Complexity
- Time complexity:
- Space complexity:
2. Hash Map (Two Pass)
Intuition
We 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:
- Pass 1: Create a copy of every node (just values), and store the mapping:
original_node → copied_node - Pass 2: Use this map to connect
nextandrandompointers for each copied node.
This guarantees all pointers are valid and no node is duplicated.
Algorithm
- Create a hash map
oldToCopy, mapping each original node to its copied node.
Includenull -> nullfor convenience. - First pass: iterate through the original list
- Create a copy of each node.
- Store the mapping in
oldToCopy.
- Second pass: iterate again
- Set
copy.nextusingoldToCopy[original.next]. - Set
copy.randomusingoldToCopy[original.random].
- Set
- Return the copied version of the head using
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]Time & Space Complexity
- Time complexity:
- Space complexity:
3. Hash Map (One Pass)
Intuition
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 nodeoldToCopy[cur.next]→ gives the copy ofcur.next(created if not present)oldToCopy[cur.random]→ gives the copy ofcur.random
This lets us fill val, next, and random in one single pass.
Algorithm
- Create a hash map
oldToCopythat returns a new empty node whenever we access a key that doesn't exist yet. - Set
oldToCopy[null] = nullsonextorrandomcan safely point tonull. - Traverse the list once:
- Set the copied node's value.
- Link its
nextpointer using the map. - Link its
randompointer using the map.
- Return the copied head from the hash 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]Time & Space Complexity
- Time complexity:
- Space complexity:
4. Space Optimized - I
Intuition
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:
- Set
randomof the copied node:A'.random = A.random.next - Finally separate the two lists again.
This gives a perfect clone using O(1) extra space.
Algorithm
Interleave copy nodes
- For each original node
A, create a copyA'and insert it right afterA.
- For each original node
Assign random pointers
- For each original node
A, ifA.randomexists, then setA'.random = A.random.next.
- For each original node
Unweave the lists
- Restore the original list.
- Extract all the copy nodes into a separate new list.
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 newHeadTime & Space Complexity
- Time complexity:
- Space complexity:
- extra space.
- for the output.
5. Space Optimized - II
Intuition
This 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:
- We create its copy and store it in the original node’s
randompointer. - The copy’s
nextpointer initially points to whatever the original node’srandomwas pointing to.
This allows us to:
- Create all copies without extra memory.
- Fix the random pointers of the copied nodes using the original structure.
- Reconnect the next pointers to build the final deep-copy list.
- Restore the original list.
Everything happens using only pointer manipulations — no extra arrays, no hash map.
Algorithm
Create all copied nodes
- For each original node
A, create a copyA'. - Store this copy inside
A.random. - Set
A'.next = A.random_old.
- For each original node
Fix random pointers of copies
- For each original node
A, setA'.random = A.random_old.random'(if exists).
- For each original node
Extract the copied list and restore original list
- For each original node
A:- Restore
A.random = original_random. - Connect
A'.nextto the next copied node.
- Restore
- For each original 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 newHeadTime & Space Complexity
- Time complexity:
- Space complexity:
- extra space.
- for the output.
Common Pitfalls
Creating Multiple Copies of the Same Node
Without 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]Forgetting to Handle Null Random Pointers
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 accessNot Restoring Original List in Space-Optimized Solutions
In 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)
Sign in to join the discussion