Clone Linked List with Random Pointer in DSA Python - Time & Space Complexity
When cloning a linked list with random pointers, we want to know how the time needed grows as the list gets longer.
We ask: How many steps does it take to copy all nodes and their random links?
Analyze the time complexity of the following code snippet.
class Node:
def __init__(self, val, next=None, random=None):
self.val = val
self.next = next
self.random = random
def clone_linked_list(head):
if not head:
return None
# Step 1: Insert cloned nodes
curr = head
while curr:
new_node = Node(curr.val, curr.next)
curr.next = new_node
curr = new_node.next
# Step 2: Assign random pointers
curr = head
while curr:
if curr.random:
curr.next.random = curr.random.next
curr = curr.next.next
# Step 3: Separate the lists
curr = head
clone_head = head.next
while curr:
clone = curr.next
curr.next = clone.next
if clone.next:
clone.next = clone.next.next
curr = curr.next
return clone_head
This code clones a linked list where each node has a next and a random pointer, creating a new list with the same structure.
Identify the loops, recursion, array traversals that repeat.
- Primary operation: Three separate while loops each traverse the entire list once.
- How many times: Each loop runs approximately n times, where n is the number of nodes.
Each loop goes through all nodes once, so the total steps add up but grow linearly with the list size.
| Input Size (n) | Approx. Operations |
|---|---|
| 10 | About 30 steps (3 loops x 10 nodes) |
| 100 | About 300 steps |
| 1000 | About 3000 steps |
Pattern observation: The total work grows directly in proportion to the number of nodes.
Time Complexity: O(n)
This means the time to clone the list grows in a straight line as the list gets longer.
[X] Wrong: "Because there are three loops, the time complexity is O(n³)."
[OK] Correct: The loops run one after another, not nested inside each other, so their times add up, not multiply.
Understanding this linear time cloning method shows you can handle complex pointer structures efficiently, a skill valued in many coding challenges.
"What if we used a hash map to store old-to-new node mappings instead of inserting cloned nodes inline? How would the time complexity change?"
Practice
random pointer in addition to the usual next pointer?Solution
Step 1: Understand the structure of the linked list
The linked list has two pointers:nextandrandom. Thenextpointer points to the next node in sequence, while therandompointer can point to any node or null.Step 2: Identify the cloning challenge
When cloning, copyingnextpointers is straightforward, butrandompointers must point to the corresponding cloned nodes, not the original nodes.Final Answer:
Ensuring the random pointers in the cloned list point to the correct cloned nodes -> Option CQuick Check:
Random pointer cloning challenge = Ensuring therandompointers in the cloned list point to the correct cloned nodes [OK]
- Ignoring random pointers and copying only next pointers
- Setting random pointers to original nodes instead of cloned nodes
- Assuming random pointers are always null or sequential
curr in a linked list with random pointers?Solution
Step 1: Understand the Node constructor
The Node constructor takes a value to create a new node. To clone a node, we pass the value of the current node.Step 2: Analyze each option
new_node = Node(curr.val) passescurr.val, which is the node's value. Options A, C, and D pass node objects or pointers, which is incorrect for creating a new node with the same value.Final Answer:
new_node = Node(curr.val) -> Option BQuick Check:
Node creation with value = new_node = Node(curr.val) [OK]
- Passing the node object instead of its value
- Passing next or random pointers instead of value
- Confusing node creation syntax
class Node:
def __init__(self, val, next=None, random=None):
self.val = val
self.next = next
self.random = random
def print_list(head):
curr = head
result = []
while curr:
rand_val = curr.random.val if curr.random else None
result.append((curr.val, rand_val))
curr = curr.next
print(result)
# Original list
node1 = Node(1)
node2 = Node(2)
node3 = Node(3)
node1.next = node2
node2.next = node3
node1.random = node3
node2.random = node1
node3.random = node2
# Cloned list (shallow copy for example)
clone1 = Node(1)
clone2 = Node(2)
clone3 = Node(3)
clone1.next = clone2
clone2.next = clone3
clone1.random = node3
clone2.random = node1
clone3.random = node2
print_list(clone1)Solution
Step 1: Understand the random pointers in the cloned list
The cloned nodes' random pointers point to the original nodes (node3, node1, node2), not the cloned nodes.Step 2: Trace the print_list output
For clone1: val=1, random points to node3 with val=3.
For clone2: val=2, random points to node1 with val=1.
For clone3: val=3, random points to node2 with val=2.
So the printed list is [(1, 3), (2, 1), (3, 2)].Final Answer:
[(1, 3), (2, 1), (3, 2)] -> Option AQuick Check:
Random pointers show original node values = [(1, 3), (2, 1), (3, 2)] [OK]
- Assuming random points to cloned nodes
- Printing None for random pointers incorrectly
- Mixing up node values in output
def clone_list(head):
if not head:
return None
curr = head
# Step 1: Create new nodes and insert them after original nodes
while curr:
new_node = Node(curr.val)
new_node.next = curr.next
curr.next = new_node
curr = new_node.next
# Step 2: Assign random pointers for new nodes
curr = head
while curr:
if curr.random:
curr.next.random = curr.random
curr = curr.next.next
# Step 3: Separate the cloned list
curr = head
clone_head = head.next
while curr:
clone = curr.next
curr.next = clone.next
if clone.next:
clone.next = clone.next.next
curr = curr.next
return clone_headSolution
Step 1: Analyze random pointer assignment
In Step 2, the code assignscurr.next.random = curr.random. Butcurr.randompoints to an original node, so the cloned node's random pointer points to an original node, not the cloned one.Step 2: Correct assignment
The correct assignment should becurr.next.random = curr.random.nextto point to the cloned node corresponding tocurr.random.Final Answer:
Random pointers are assigned incorrectly by missing .next on curr.random -> Option AQuick Check:
Random pointer bug = missing .next on curr.random [OK]
- Assigning random pointer directly to original node
- Forgetting to separate cloned list
- Not inserting cloned nodes after originals
Solution
Step 1: Understand space and time constraints
O(n) time means one or two passes over the list. O(1) extra space means no extra data structures like hash maps.Step 2: Analyze each approach
Use a hash map to map original nodes to cloned nodes. uses extra space (hash map), so not O(1) space.
Insert cloned nodes after original nodes, assign random pointers using next pointers, then separate lists. inserts cloned nodes between original nodes, uses next pointers to assign random pointers correctly, then separates the lists. This uses no extra space and runs in O(n).
Create a new list by copying values and random pointers by traversing twice. requires extra space or multiple traversals without the in-place trick.
Reverse the original list, clone nodes, then reverse back. is unrelated and does not solve the problem efficiently.Final Answer:
Insert cloned nodes after original nodes, assign random pointers using next pointers, then separate lists. -> Option DQuick Check:
In-place cloning with next pointers = Insert cloned nodes after original nodes, assign random pointers using next pointers, then separate lists. [OK]
- Using hash maps increases space complexity
- Trying to clone without linking cloned nodes properly
- Ignoring the need to separate cloned list at the end
Ready to prove it?
Pass the test and this pattern moves unknown
