Clone Linked List with Random Pointer in DSA Go - Time & Space Complexity
We want to understand how the time needed to clone a linked list with random pointers changes as the list grows.
Specifically, how many steps does it take to copy all nodes and their random links?
Analyze the time complexity of the following code snippet.
// Node structure
type Node struct {
Val int
Next *Node
Random *Node
}
func cloneList(head *Node) *Node {
if head == nil {
return nil
}
// Step 1: Insert cloned nodes
curr := head
for curr != nil {
newNode := &Node{Val: curr.Val}
newNode.Next = curr.Next
curr.Next = newNode
curr = newNode.Next
}
// Step 2: Assign random pointers
curr = head
for curr != nil {
if curr.Random != nil {
curr.Next.Random = curr.Random.Next
}
curr = curr.Next.Next
}
// Step 3: Separate cloned list
curr = head
cloneHead := head.Next
for curr != nil {
clone := curr.Next
curr.Next = clone.Next
if clone.Next != nil {
clone.Next = clone.Next.Next
}
curr = curr.Next
}
return cloneHead
}
This code clones a linked list where each node has a next and a random pointer, creating a deep copy.
Identify the loops, recursion, array traversals that repeat.
- Primary operation: Three separate loops each traverse the entire list once.
- How many times: Each loop runs once over all n nodes, so about 3n steps total.
As the list size grows, the total steps grow roughly in direct proportion.
| 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 operations increase linearly as the list size increases.
Time Complexity: O(n)
This means the time to clone the list grows in a straight line with the number of nodes.
[X] Wrong: "Because there are nested steps, the time must be quadratic O(n²)."
[OK] Correct: Each loop runs separately over the list, not inside each other, so the total steps add up linearly, not multiply.
Understanding this linear time complexity helps you explain efficient cloning of complex linked structures clearly and confidently.
"What if we used a hash map to store cloned nodes instead of inserting them inline? How would the time complexity change?"
Practice
Solution
Step 1: Understand the structure of the linked list
The linked list has two pointers per node: next and random. Both need to be copied to clone the list correctly.Step 2: Identify the cloning challenge
Copying next pointers is straightforward, but random pointers can point anywhere, so they must be handled carefully to maintain correct references.Final Answer:
Copying both the next and random pointers correctly -> Option DQuick Check:
Cloning requires both pointers copied correctly [OK]
- Copying only next pointers and ignoring random
- Assuming random pointers follow next pointers
- Not handling null random pointers
Solution
Step 1: Understand pointer usage in Go structs
Next and Random should be pointers to Node to link nodes properly, so use *Node type.Step 2: Check each option for correct pointer types
type Node struct { Val int; Next *Node; Random *Node } uses *Node for both Next and Random, which is correct. Others use wrong types or non-pointers.Final Answer:
type Node struct { Val int; Next *Node; Random *Node } -> Option BQuick Check:
Use pointers for linked nodes in Go structs [OK]
- Using value type instead of pointer for next/random
- Using wrong pointer types like *int
- Mixing pointer and non-pointer types incorrectly
type Node struct {
Val int
Next *Node
Random *Node
}
func cloneList(head *Node) *Node {
if head == nil {
return nil
}
// Step 1: Insert cloned nodes
curr := head
for curr != nil {
newNode := &Node{Val: curr.Val}
newNode.Next = curr.Next
curr.Next = newNode
curr = newNode.Next
}
// Step 2: Copy random pointers
curr = head
for curr != nil {
if curr.Random != nil {
curr.Next.Random = curr.Random.Next
}
curr = curr.Next.Next
}
// Step 3: Separate cloned list
curr = head
cloneHead := head.Next
cloneCurr := cloneHead
for curr != nil {
curr.Next = curr.Next.Next
if cloneCurr.Next != nil {
cloneCurr.Next = cloneCurr.Next.Next
}
curr = curr.Next
cloneCurr = cloneCurr.Next
}
return cloneHead
}
// Original list: 1 -> 2 -> 3 -> nil
// Random pointers: 1.random = 3, 2.random = 1, 3.random = 2
func main() {
n3 := &Node{Val: 3}
n2 := &Node{Val: 2, Next: n3}
n1 := &Node{Val: 1, Next: n2}
n1.Random = n3
n2.Random = n1
n3.Random = n2
cloned := cloneList(n1)
curr := cloned
for curr != nil {
fmt.Print(curr.Val)
if curr.Next != nil {
fmt.Print(" -> ")
}
curr = curr.Next
}
fmt.Println(" -> nil")
}Solution
Step 1: Understand cloning steps
The code clones nodes by inserting new nodes after originals, copies random pointers, then separates cloned list.Step 2: Trace the cloned list values
Original list is 1->2->3. Cloned list copies values in same order: 1->2->3.Final Answer:
1 -> 2 -> 3 -> nil -> Option AQuick Check:
Cloned list values match original order [OK]
- Assuming cloned list reverses order
- Confusing random pointer order with next pointer order
- Not separating cloned list properly
func cloneList(head *Node) *Node {
if head == nil {
return nil
}
curr := head
for curr != nil {
newNode := &Node{Val: curr.Val}
newNode.Next = curr.Next
curr.Next = newNode
curr = curr.Next
}
curr = head
for curr != nil {
if curr.Random != nil {
curr.Next.Random = curr.Random.Next
}
curr = curr.Next.Next
}
curr = head
cloneHead := head.Next
cloneCurr := cloneHead
for curr != nil {
curr.Next = curr.Next.Next
if cloneCurr.Next != nil {
cloneCurr.Next = cloneCurr.Next.Next
}
curr = curr.Next
cloneCurr = cloneCurr.Next
}
return cloneHead
}Solution
Step 1: Analyze the first loop for inserting cloned nodes
After inserting newNode, curr should move two steps to skip the new node and reach next original node.Step 2: Identify incorrect pointer update
The code uses curr = curr.Next, which moves to the new node again, causing an infinite loop or incorrect behavior.Final Answer:
In the first loop, curr = curr.Next should be curr = curr.Next.Next -> Option AQuick Check:
Move curr two steps after inserting clone [OK]
- Moving curr only one step causes infinite loop
- Not updating random pointers properly
- Forgetting to separate cloned list
Solution
Step 1: Understand cloning and reversing separately
Cloning creates a copy with correct next and random pointers. Reversing changes next pointers order.Step 2: Combine cloning and reversing correctly
Clone first to preserve random pointers, then reverse cloned list's next pointers without affecting random pointers.Final Answer:
Clone the list first, then reverse the cloned list by changing next pointers. -> Option CQuick Check:
Clone then reverse next pointers to keep random intact [OK]
- Reversing original list breaks random pointer references
- Reversing random pointers instead of next pointers
- Assuming reversing is impossible with random pointers
Ready to prove it?
Pass the test and this pattern moves unknown
