Core challenge: a copied pointer still points into the original structure. You must remap every pointer to the corresponding clone.
HashMap approach (O(n) space): two passes — first create all clones, then wire next/random using the map.
Weaving approach (O(1) space): interleave clones between originals, set randoms via A.random.next, then un-weave.
Forward reference problem: when processing node A, A.random's clone may not exist yet. HashMap or weaving solves this.
Production risk: shallow copy (pointing random at original nodes) causes silent data corruption when the original is mutated.
This is a graph-traversal problem disguised as a linked-list problem — the random pointer breaks linearity.
✦ Definition~90s read
What is Clone a Linked List with Random Pointers?
Each node in this linked list has a next pointer (to the following node) and a random pointer (to any node in the list, or None). Cloning it naively requires two passes: one to create new nodes, and a second to set random pointers. A hash map from original_node -> cloned_node makes both passes easy and is the standard O(n) time, O(n) space approach.
★
Imagine you have a classroom seating chart where every student has a 'best friend' who can sit anywhere in the room — not just next to them.
The key challenge: when you create a new node and want to set its random pointer, the target of the random pointer may not have been created yet. The hash map solves this by building all new nodes first, then linking them.
Plain-English First
Imagine you have a classroom seating chart where every student has a 'best friend' who can sit anywhere in the room — not just next to them. Now your teacher asks you to photocopy the entire class with all those friendships intact. You can't just copy names in order — you have to make sure every copied student's best-friend pointer aims at the right copy, not the original. That's exactly the problem: duplicating a linked list where each node has a mystery arrow that can point to any node, or to nothing at all.
Deep-copying a linked list with random pointers is a pointer-remapping problem. Each node has two outgoing references: next (sequential) and random (arbitrary). A naive copy creates new nodes but leaves random pointers pointing at the original structure — a shallow copy that corrupts when the original is mutated.
The random pointer breaks the list's linearity. When processing node A, its random target (node 7) may not have been cloned yet. Two strategies solve this: a HashMap that pre-creates all clones before wiring pointers, or an interweaving trick that places each clone adjacent to its original so the clone is always reachable via original.next.
This problem appears in serialization of graph-like structures, deep-copying editor undo/redo trees, and duplicating compiler adjacency representations. In production, a shallow copy bug manifests as cross-contamination between independent data structures — one structure's mutation silently affects another.
What is Clone a Linked List with Random Pointers? — Plain English
Each node in this linked list has a next pointer (to the following node) and a random pointer (to any node in the list, or None). Cloning it naively requires two passes: one to create new nodes, and a second to set random pointers. A hash map from original_node -> cloned_node makes both passes easy and is the standard O(n) time, O(n) space approach.
The key challenge: when you create a new node and want to set its random pointer, the target of the random pointer may not have been created yet. The hash map solves this by building all new nodes first, then linking them.
The Photocopy Analogy
Photocopying a seating chart copies names in order — but the 'best friend' arrows still point at the original students.
You need a name-to-copy mapping so every arrow can be redirected to the photocopy.
Without the mapping, the photocopy is a shallow copy — it shares state with the original.
The HashMap is that name-to-copy mapping. The weaving trick achieves the same without explicit storage.
Production Insight
The forward-reference problem is the core complexity. In a plain linked list, you process nodes sequentially and each node's next target is always the node you just created. With random pointers, the target may be anywhere — behind, ahead, or self approach (O(n-referencing. The HashMap pre-creation pass eliminates this by ensuring every clone exists before any pointer is wired.
Key Takeaway
Random pointers break linearity. The forward-reference problem requires either pre-creation (HashMap) or structural interleaving (weaving) to remap pointers correctly.
thecodeforge.io
Clone Linked List with Random Pointers
Clone Linked List Random Pointers
How Clone a Linked List with Random Pointers Works — Step by Step) time, O(n) space):
1. Pass 1 — create all new nodes:
For each node in the original list: map[original] = Node(original.val).
2. Pass 2 — set pointers:
For each node in the original list:
map[node].next = map[node.next] (or None if node.next is None).
map[node].random = map[node.random] (or None).
3. Return map[head].
O(1) space approach (weaving):
1. Insert each cloned node right after its original: A -> A' -> B -> B' -> ...
2. Set random pointers: A'.random = A.random.next (the clone of A's random).
3. Un-weave: restore original list and extract cloned list.
Hash map, making it unsafe for concurrent access.
Why the Weaving Trick Works
After interleaving, every clone is exactly one hop from its original: original.next = clone.
So clone.random = original.random.next — the clone of the random target is always its next node.
No HashMap needed — the interleaved structure IS the mapping.
The un-weave step restores both lists by separating odd-positioned (original) and even-positioned (clone) nodes.
Production Insight
The weaving approach temporarily mutates the original list by inserting clone nodes. In a concurrent environment, this mutation is visible to other threads — they see a list twice as long with interleaved clone nodes. If the original list is shared, use the HashMap approach to avoid mutation. The O(1) space saving is not worth a concurrency bug.
Key Takeaway
HashMap is the safe default — clean, debuggable, no mutation of the original. Weaving is the space-optimized alternative but mutates the original list temporarily
Choosing Between HashMap and Weaving
IfMemory is constrained (embedded systems, high-throughput services)
→
UseUse weaving — O(1) extra space. More complex but avoids HashMap allocation.
IfClarity is prioritized (prototyping, code review, teaching)
→
UseUse HashMap — two clean passes, easy to reason about, easy to debug.
IfInterview context — asked for O(1) space
→
UseImplement weaving. Mention the three-pass structure: interleave, set randoms, un-weave.
IfProduction code — must not corrupt the original list
→
UseUse HashMap. Weaving temporarily modifies the original list structure, which is unsafe if the original is concurrently accessed.
Cloned list: 1'->2'->3'. 1'.random=3', 2'.random=1', 3'.random=2'. Fully independent deep copy.
Pro Tip: Trace With Pointer Addresses
Print System.identityHashCode(original) and System.identityHashCode(clone.random).
If they match, clone.random points at the original — shallow copy bug.
If clone.random's hash matches map.get(original.random)'s hash, the copy is correct.
This works because identity hash is based on memory address, not value.
Production Insight
The trace reveals the forward-reference resolution: when setting map[1].random = map[3], node 3's clone already exists because Pass 1 created all clones. Without Pass 1, you would need a lazy-creation mechanism (get-or-create) to handle the case where map[3] does not yet exist.
Key Takeaway
Tracing with identity hashes detects shallow-copy bugs instantly. The two-pass structure (create all, then wire all) eliminates forward-reference problems.
Implementation
The implementation uses two passes. In the first pass, iterate the original list and create a new node for each original node, storing the mapping in a hash map. In the second pass, use the hash map to wire up the next and random pointers on each new node. This runs in O(n) time and O(n) space. The alternative O(1) space approach interleaves cloned nodes between original nodes, sets random pointers, then splits the two lists apart in a third pass.
CloneLinkedListWithRandom.javaJAVA
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
package io.thecodeforge.list;
import java.util.HashMap;
import java.util.Map;
/**
* Deep clone a linked list with random pointers.
* HashMap approach: O(n) time, O(n) space.
* Weaving approach: O(n) time, O(1) space.
*/
publicclassCloneLinkedListWithRandom {
staticclassNode {
int val;
Node next;
Node random;
Node(int val) {
this.val = val;
this.next = null;
this.random = null;
}
}
// ─── Approach 1: HashMap (O(n) space) ──────────────────────────────────publicstaticNodecloneWithHashMap(Node head) {
if (head == null) returnnull;
// Pass 1: create all clone nodes, store mapping original -> cloneMap<Node, Node> cloneMap = newHashMap<>();
Node current = head;
while (current != null) {
cloneMap.put(current, newNode(current.val));
current = current.next;
}
// Pass 2: wire next and random pointers using the map
current = head;
while (current != null) {
cloneMap.get(current).next = cloneMap.get(current.next);
cloneMap.get(current).random = cloneMap.get(current.random);
current = current.next;
}
return cloneMap.get(head);
}
// ─── Approach 2: Weaving (O(1) space) ──────────────────────────────────publicstaticNodecloneWithWeaving(Node head) {
if (head == null) returnnull;
// Step 1: Interleave — insert clone after each original// Original: A -> B -> C// After: A -> A' -> B -> B' -> C -> C'Node current = head;
while (current != null) {
Node clone = newNode(current.val);
clone.next = current.next;
current.next = clone;
current = clone.next;
}
// Step 2: Set random pointers// A'.random = A.random.next (the clone of A's random target)
current = head;
while (current != null) {
if (current.random != null) {
current.next.random = current.random.next;
}
current = current.next.next;
}
// Step 3: Un-weave — separate original and clone listsNode cloneHead = head.next;
current = head;
while (current != null) {
Node clone = current.next;
current.next = clone.next;
clone.next = (clone.next != null) ? clone.next.next : null;
current = current.next;
}
return cloneHead;
}
// ─── Verification ──────────────────────────────────────────────────────publicstaticbooleanverifyClone(Node original, Node clone) {
// Verify no original nodes are reachable from the clone// by checking identity hashes don't overlapNode origCurrent = original;
Node cloneCurrent = clone;
while (origCurrent != null && cloneCurrent != null) {
if (origCurrent == cloneCurrent) return false; // same objectif (cloneCurrent.random != null && cloneCurrent.random == origCurrent) returnfalse;
origCurrent = origCurrent.next;
cloneCurrent = cloneCurrent.next;
}
return cloneCurrent == null && origCurrent == null; // same length
}
publicstaticvoidmain(String[] args) {
// Build: 1 -> 2 -> 3// Random: 1->3, 2->1, 3->2Node n1 = newNode(1);
Node n2 = newNode(2);
Node n3 = newNode(3);
n1.next = n2; n2.next = n3;
n1.random = n3; n2.random = n1; n3.random = n2;
// Clone with HashMapNode clone1 = cloneWithHashMap(n1);
System.out.println("HashMap clone:");
System.out.println(" Values: " + clone1.val + " -> " + clone1.next.val + " -> " + clone1.next.next.val);
System.out.println(" Random: 1->" + clone1.random.val + ", 2->" + clone1.next.random.val + ", 3->" + clone1.next.next.random.val);
System.out.println(" Independent: " + verifyClone(n1, clone1));
System.out.println(" Identity diff: " + (clone1 != n1 && clone1.random != n1.random));
// Clone with weavingNode clone2 = cloneWithWeaving(n1);
System.out.println("\nWeaving clone:");
System.out.println(" Values: " + clone2.val + " -> " + clone2.next.val + " -> " + clone2.next.next.val);
System.out.println(" Random: 1->" + clone2.random.val + ", 2->" + clone2.next.random.val + ", 3->" + clone2.next.next.random.val);
System.out.println(" Independent: " + verifyClone(n1, clone2));
// Edge case: null listNode nullClone = cloneWithHashMap(null);
System.out.println("\nNull list clone: " + (nullClone == null ? "null" : "ERROR"));
// Edge case: single node with self-randomNode self = newNode(42);
self.random = self;
Node selfClone = cloneWithHashMap(self);
System.out.println("Self-random clone: val=" + selfClone.val + ", random->val=" + selfClone.random.val + ", isSameObject=" + (selfClone == selfClone.random));
}
}
Weaving: original list is modified during cloning. Unsafe for concurrent access.
HashMap: original list is never modified. Safe for concurrent read access.
If the original list is a field in a shared object, weaving introduces a data race.
The O(1) space saving of weaving is not worth a concurrency bug in production.
Production Insight
The verifyClone() method — which checks that no original nodes are reachable from the clone — should be enabled in debug builds and integration tests. It catches shallow-copy bugs where random pointers still reference the original structure. This is the most common production bug in clone implementations.
Key Takeaway
Two approaches: HashMap (O(n) space, safe, debuggable) and weaving (O(1) space, mutates original, unsafe for concurrent access). The verifyClone() invariant check catches shallow-copy bugs. Always handle null random pointers explicitly.
Edge Cases to Handle
IfHead is null (empty list)
→
UseReturn null immediately. Both approaches handle this.
IfSingle node with random pointing to itself
→
UseHashMap: map[node].random = map[node] (points to its own clone). Weaving: clone.random = node.next = clone (after interleaving). Both correct.
IfAll random pointers are null
→
UseBoth approaches handle null randoms. The clone's random pointers are all null.
IfRandom pointers form a cycle (1->3, 3->1)
→
UseBoth approaches handle cycles. The HashMap approach never traverses random pointers — only next. Cycles are irrelevant.
IfRandom pointer points to a node not in the list
→
UseThis should not happen by problem definition. If it does, the HashMap returns null for that key, causing a NullPointerException. Add a null guard.
Naive Approach: Hash Map — Simple But Costly Under Load
The hash map method is the first thing most devs reach for. It works. But it's the kind of 'works' that gets you paged at 3 AM when your service starts GC-pausing every 10 seconds. Here's the deal: two passes, O(n) time, O(n) space. For each original node, you create a clone and stash it in a map. Second pass, you wire up next and random by looking up the map.
Why this matters: the random pointer is what makes this problem nasty. With a hash map, you're doing two full traversals and paying for a map that lives as long as the cloned list. That memory overhead hits hard when your linked list is 200K nodes deep and your heap is already fighting with caches.
The real cost isn't obvious until production. The hash map lookup is fast, yes — but allocation per node doubles your memory footprint. In Java, that's two objects per logical node plus the HashMap's internal array. On memory-constrained services, this pattern is a non-starter.
Returns head of cloned list. All random pointers correctly set.
Production Trap:
Hash map approach doubles memory per node. On 500K nodes, that's 1M objects. Your GC will hate you. Prefer for small-to-medium lists (< 10K nodes) where readability matters over raw performance.
Key Takeaway
When memory is cheap and list is small, hash map wins on clarity. When nodes hit six figures, you pay the tax.
Expected Approach: Weaving — O(1) Space, No Excuses
This is the move that separates junior from senior. Instead of a map, you interleave clone nodes directly into the original list. A -> A' -> B -> B' -> C -> C'. That single pass creates your 'map' for free — the clone of node X is just X.next. No allocations beyond the clones themselves.
Second pass sets random pointers: clone.random = original.random.next. That works because original.random.next is always its clone. Dirty? Yes. Efficient? Absolutely.
Third pass un-weaves: restore original list by skipping clones, extract cloned list by skipping originals. Two pointers, O(1) extra space, done.
Why seniors love this: zero additional memory for lookup structures. The pointer manipulation is exactly the kind of low-level optimization that matters when your service handles millions of requests. Also, no hash function to debug when something goes wrong — it's just pointer arithmetic on the heap.
Returns head of cloned list. Original list restored. Random pointers correct. O(1) extra space.
Senior Shortcut:
This technique works because 'next' becomes a traversal key. Think of it as an implicit hash map built into the list structure. Only works for singly-linked lists — don't try on doubly-linked or you'll corrupt prev pointers.
Key Takeaway
Weaving gives O(1) extra space by encoding the mapping in the list structure itself. Master this — it shows up in production code reviews for cache-aligned data structures.
● Production incidentPOST-MORTEMseverity: high
The Undo Stack That Cross-Contaminated Editor Sessions
Symptom
After duplicating a document, undoing an action in the copy also reverted the same action in the original. The corruption was intermittent — it only occurred when random pointers in the action graph pointed to shared sub-structures (e.g., linked formatting spans).
Assumption
The team initially suspected a shared-reference bug in the document model's serialization layer, because the corruption appeared only after duplication — not during normal editing.
Root cause
The clone function correctly deep-copied the next pointers (sequential action chain) but shallow-copied the random pointers (cross-references between non-adjacent actions). The random pointers in the cloned action graph still pointed to nodes in the original document's action graph. When the original document's actions were modified, the cloned document's random pointers followed those mutations, creating cross-contamination.
Fix
1. Replaced shallow random-pointer copy with a HashMap-based deep copy: create all clone nodes first, then remap random pointers using the original-to-clone mapping.
2. Added a post-clone invariant check: verify that no node in the cloned graph references any node in the original graph.
3. Added integration tests that mutate the original after cloning and verify the clone is unaffected.
4. Introduced a CloneContext class that encapsulates the HashMap and provides getOrCreateClone() to handle forward references lazily.
Key lesson
A deep copy must remap ALL pointers — not just next. Random/arbitrary pointers are the common oversight.
Shallow copy of random pointers creates cross-contamination between a silent corruption bug — no exception, no crash, just wrong behavior.
Post-clone invariant checks (no original nodes reachable from clone) catch shallow-copy bugs in debug builds.
The forward-reference problem (random target not yet cloned) requires either pre-creation (HashMap) or structural interleaving (weaving).
Production debug guideSymptom-driven investigation paths for clone-related incidents.5 entries
Symptom · 01
Mutating the original structure also affects the cloned structure.
→
Fix
Suspect shallow copy of random pointers. Check if random pointers in the clone reference nodes in the original. Add a post-clone invariant check.
Symptom · 02
NullPointerException when accessing random pointers in the cloned list.
→
Fix
Check if the clone function handles null random pointers. A null random in the original must map to null in the clone — not a missing map entry.
Symptom · 03
Clone function returns a list with correct next pointers but wrong random pointers.
→
Fix
Verify that the HashMap maps original nodes to clone nodes (not original to original). A common bug is using the original node as both key and value.
Symptom · 04
Infinite loop during cloning — thread spins at 100% CPU.
→
Fix
Check if the weaving approach is used on a list where random pointers create cycles. The un-weave step may loop infinitely if the original list structure is corrupted.
Symptom · 05
Memory usage doubles after cloning — expected but check for leaks.
→
Fix
Verify the HashMap is eligible for garbage collection after cloning. If the clone function stores the HashMap as a field, it prevents GC of all original nodes.
★ Linked List Clone Triage Cheat SheetFast commands to diagnose common production issues with deep-copy operations.
Original and clone share state — mutation in one affects the other.−
Immediate action
Heap dump to check if clone nodes reference original nodes via random pointers.
Commands
jcmd <pid> GC.heap_dump /tmp/heap.hprof
jmap -histo <pid> | grep Node (count Node instances — should be 2x original)
Fix now
If clone nodes reference original nodes, the random-pointer copy is shallow. Replace with HashMap-based deep copy.
NullPointerException in cloned list when accessing random pointers.+
Immediate action
Check if the clone function has null guards for random pointers.
Weave cloned nodes into the original list: A->A'->B->B'->C->C'. Set random: A'.random = A.random.next. Unweave: restore A->B->C and build A'->B'->C'. No hash map needed — the interleaved structure acts as the map. O(n) time, O(1) extra space.
Was this helpful?
02
Why can't you just copy next and random in one pass?
When you process node A and want to set A'.random to point to the clone of A.random, that clone might not exist yet. You need either a hash map (pre-create all clones first) or the weaving trick (interleave so that each node's clone is always its immediate next).
Was this helpful?
03
Does this algorithm work if random pointers form cycles?
Yes. The hash map approach creates the mapping first, then sets pointers. Cycles in random pointers don't cause infinite loops because we only traverse via the next pointer in both passes, not via random.
Was this helpful?
04
How is this different from cloning a plain linked list?
A plain linked list has only next pointers. Cloning it is a single-pass operation — create each clone and immediately set its next. The random pointer adds a second relationship that may point to any node (including forward references), requiring the HashMap or weaving technique.
Was this helpful?
05
Can Java's clone() method handle this?
No. Java's Object.clone() performs a shallow copy by default — it copies field references, not the objects they point to. For a linked list with random pointers, clone() would create new node objects but their next and random fields would still point to the original nodes. You must implement deep copy manually.
Was this helpful?
06
How do you verify a clone is correct?
Check three things: (1) the clone has the same values in the same order, (2) the clone's random pointers point to the correct clone nodes (not originals), and (3) no original node is reachable from the clone. Use System.identityHashCode to verify object identity.