Clone Linked List with Random Pointer in DSA Typescript - 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.
function cloneLinkedList(head: Node | null): Node | null {
if (!head) return null;
// Step 1: Insert cloned nodes
let curr = head;
while (curr) {
const newNode = new Node(curr.val);
newNode.next = curr.next;
curr.next = newNode;
curr = newNode.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 cloned list
curr = head;
const cloneHead = head.next;
while (curr) {
const clone = curr.next;
curr.next = clone.next;
clone.next = clone.next ? clone.next.next : null;
curr = curr.next;
}
return cloneHead;
}
This code copies 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 once over all n nodes, so about 3 x n steps total.
As the list grows, the number of steps grows roughly in direct proportion.
| Input Size (n) | Approx. Operations |
|---|---|
| 10 | About 30 steps (3 x 10) |
| 100 | About 300 steps (3 x 100) |
| 1000 | About 3000 steps (3 x 1000) |
Pattern observation: The steps 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 three loops, the time is O(n³)."
[OK] Correct: Each loop runs one after another, not nested inside each other, so their times add up, not multiply.
Understanding this linear time cloning shows you can handle complex pointer structures efficiently, a skill valued in many coding challenges.
"What if we used recursion to clone the list instead of loops? How would the time complexity change?"
Practice
Solution
Step 1: Understand the structure of the linked list
The linked list has two pointers: next and random. The random pointer can point to any node or null.Step 2: Identify the cloning challenge
While copying nodes is straightforward, correctly assigning random pointers in the cloned list is tricky because they may point to nodes not yet cloned.Final Answer:
Maintaining the correct random pointer connections in the cloned list -> Option CQuick Check:
Random pointer cloning = main challenge [OK]
- Ignoring random pointers during cloning
- Assuming random pointers are always null
- Copying only next pointers
Solution
Step 1: Check for nullable pointers
Both next and random pointers can be null, so their types must allow null.Step 2: Verify constructor and property declarations
class Node { val: number; next: Node | null; random: Node | null; constructor(val?: number) { this.val = val ?? 0; this.next = null; this.random = null; } } correctly uses 'Node | null' and initializes properties properly with optional val.Final Answer:
Option A correctly declares nullable next and random pointers with proper constructor -> Option AQuick Check:
Nullable pointers + constructor = class Node { val: number; next: Node | null; random: Node | null; constructor(val?: number) { this.val = val ?? 0; this.next = null; this.random = null; } } [OK]
- Not allowing null for next or random
- Using interface without constructor
- Missing property initializations
class Node {
val: number;
next: Node | null;
random: Node | null;
constructor(val?: number) {
this.val = val ?? 0;
this.next = null;
this.random = null;
}
}
function cloneList(head: Node | null): Node | null {
if (!head) return null;
let curr = head;
while (curr) {
const copy = new Node(curr.val);
copy.next = curr.next;
curr.next = copy;
curr = copy.next;
}
curr = head;
while (curr) {
if (curr.random) curr.next!.random = curr.random.next;
curr = curr.next!.next;
}
curr = head;
const pseudoHead = new Node(0);
let copyCurr = pseudoHead;
while (curr) {
copyCurr.next = curr.next;
curr.next = curr.next!.next;
copyCurr = copyCurr.next;
curr = curr.next;
}
return pseudoHead.next;
}
// Original list: 1 -> 2 -> null
// Node 1 random points to Node 2
// Node 2 random points to Node 1
const node1 = new Node(1);
const node2 = new Node(2);
node1.next = node2;
node1.random = node2;
node2.random = node1;
const clonedHead = cloneList(node1);
console.log(clonedHead!.val);
console.log(clonedHead!.random!.val);
console.log(clonedHead!.next!.val);
console.log(clonedHead!.next!.random!.val);Solution
Step 1: Understand cloning steps
The code interleaves cloned nodes, assigns random pointers, then separates the cloned list.Step 2: Trace output values
clonedHead.val = 1; clonedHead.random.val = 2; clonedHead.next.val = 2; clonedHead.next.random.val = 1.Final Answer:
1 2 2 1 -> Option AQuick Check:
Cloned values and randoms match original pattern [OK]
- Not updating random pointers correctly
- Mixing original and cloned nodes in output
- Confusing next and random values
function cloneList(head: Node | null): Node | null {
if (!head) return null;
let curr = head;
while (curr) {
const copy = new Node(curr.val);
copy.next = curr.next;
curr.next = copy;
curr = curr.next;
}
curr = head;
while (curr) {
if (curr.random) curr.next!.random = curr.random.next;
curr = curr.next!.next;
}
curr = head;
const pseudoHead = new Node(0);
let copyCurr = pseudoHead;
while (curr) {
copyCurr.next = curr.next;
curr.next = curr.next!.next;
copyCurr = copyCurr.next;
curr = curr.next;
}
return pseudoHead.next;
}Solution
Step 1: Analyze the first while loop
Inside the loop, curr is updated as curr = curr.next, but curr.next is the cloned node, so curr never moves past cloned nodes, causing infinite loop.Step 2: Correct the pointer update
It should be curr = copy.next to move to the next original node.Final Answer:
In the first while loop, curr is updated incorrectly causing an infinite loop -> Option BQuick Check:
Incorrect curr update = infinite loop [OK]
- Updating curr to curr.next instead of curr.next.next
- Skipping random pointer assignment
- Not separating cloned list
Solution
Step 1: Understand random pointer complexities
Random pointers can be null or form cycles, so direct assignment during cloning is unsafe.Step 2: Use a hash map for safe cloning
Mapping original nodes to cloned nodes allows assigning random pointers correctly after all nodes are cloned, handling nulls and cycles safely.Final Answer:
Use a hash map to store original to cloned node mappings and assign random pointers after cloning all nodes -> Option DQuick Check:
Hash map handles nulls and cycles safely [OK]
- Ignoring null random pointers
- Trying to assign randoms in one pass without map
- Reversing list unnecessarily
Ready to prove it?
Pass the test and this pattern moves unknown
