Advanced 3 min · March 05, 2026

Clone Linked List Random Pointers — Undo Stack Corruption

Shallow-copying random pointers caused cross-contamination in undo stacks.

N
Naren · Founder
Plain-English first. Then code. Then the interview question.
About
 ● Production Incident 🔎 Debug Guide
Quick Answer
  • 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.
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.

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.
IfList may have null random pointers
UseBoth approaches handle null randoms. HashMap: map.get(null) returns null. Weaving: A.random.next would NPE — add null guard.

Worked Example — Tracing the Algorithm

List: 1 -> 2 -> 3 -> None. Random pointers: 1.random=3, 2.random=1, 3.random=2.

Pass 1 — create map: map[1] = Node(1), map[2] = Node(2), map[3] = Node(3).

Pass 2 — set pointers: map[1].next = map[2]. map[1].random = map[3]. map[2].next = map[3]. map[2].random = map[1]. map[3].next = None. map[3].random = map[2].

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.
 */
public class CloneLinkedListWithRandom {

    static class Node {
        int val;
        Node next;
        Node random;

        Node(int val) {
            this.val = val;
            this.next = null;
            this.random = null;
        }
    }

    // ─── Approach 1: HashMap (O(n) space) ──────────────────────────────────
    public static Node cloneWithHashMap(Node head) {
        if (head == null) return null;

        // Pass 1: create all clone nodes, store mapping original -> clone
        Map<Node, Node> cloneMap = new HashMap<>();
        Node current = head;
        while (current != null) {
            cloneMap.put(current, new Node(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) ──────────────────────────────────
    public static Node cloneWithWeaving(Node head) {
        if (head == null) return null;

        // 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 = new Node(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 lists
        Node 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 ──────────────────────────────────────────────────────
    public static boolean verifyClone(Node original, Node clone) {
        // Verify no original nodes are reachable from the clone
        // by checking identity hashes don't overlap
        Node origCurrent = original;
        Node cloneCurrent = clone;

        while (origCurrent != null && cloneCurrent != null) {
            if (origCurrent == cloneCurrent) return false; // same object
            if (cloneCurrent.random != null && cloneCurrent.random == origCurrent) return false;
            origCurrent = origCurrent.next;
            cloneCurrent = cloneCurrent.next;
        }
        return cloneCurrent == null && origCurrent == null; // same length
    }

    public static void main(String[] args) {
        // Build: 1 -> 2 -> 3
        // Random: 1->3, 2->1, 3->2
        Node n1 = new Node(1);
        Node n2 = new Node(2);
        Node n3 = new Node(3);
        n1.next = n2; n2.next = n3;
        n1.random = n3; n2.random = n1; n3.random = n2;

        // Clone with HashMap
        Node 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 weaving
        Node 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 list
        Node nullClone = cloneWithHashMap(null);
        System.out.println("\nNull list clone: " + (nullClone == null ? "null" : "ERROR"));

        // Edge case: single node with self-random
        Node self = new Node(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));
    }
}
Output
HashMap clone:
Values: 1 -> 2 -> 3
Random: 1->3, 2->1, 3->2
Independent: true
Identity diff: true
Weaving clone:
Values: 1 -> 2 -> 3
Random: 1->3, 2->1, 3->2
Independent: true
Null list clone: null
Self-random clone: val=42, random->val=42, isSameObject=true
Watch Out: Weaving Mutates the Original
  • 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.
● 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.
Commands
jcmd <pid> Thread.print (confirm stack trace location)
grep -A5 'random' src/ (find all random pointer assignments)
Fix now
Add null check: if (original.random != null) clone.random = map.get(original.random); else clone.random = null;
Heap grows after cloning but original should be GC-eligible.+
Immediate action
Heap dump to check if the HashMap from the clone operation still holds references to original nodes.
Commands
jcmd <pid> GC.heap_dump /tmp/heap.hprof
jmap -dump:format=b,file=/tmp/heap.hprof <pid>
Fix now
Ensure the HashMap is a local variable, not a class field. It should be eligible for GC after the clone method returns.
Three Approaches to Cloning a Linked List with Random Pointers
AspectHashMap (Two-Pass)Weaving (Interleave)Recursive Memoised
Time ComplexityO(n)O(n)O(n)
Space ComplexityO(n) — HashMap storageO(1) — no extra data structureO(n) — call stack + memo map
Mutates Original?NoYes — temporarily inserts clone nodesNo
Thread-Safe for Shared Original?Yes — original is read-onlyNo — original is modified during cloneYes — original is read-only
Code ComplexitySimple — two clean passesModerate — three passes with interleavingModerate — recursion with memoisation
Handles Cycles in Random?Yes — never traverses randomYes — never traverses randomRequires memo map to avoid infinite recursion
Handles Null Random?Yes — map.get(null) returns nullYes — with null guardYes — base case handles null
Stack Overflow RiskNoneNoneYes — deep recursion on long lists
DebuggabilityEasy — map contents are inspectableHarder — interleaved structure is confusingModerate — recursion trace helps
Interview PreferenceExpected as first answerExpected as O(1) space follow-upAcceptable but rarely asked

Key takeaways

1
Two-pass hash map
first create all new nodes, then set next and random. O(n) time, O(n) space.
2
O(1) space via weaving
interleave clones into original, set randoms, then un-weave.
3
The hash map key is the original node pointer; the value is the cloned node.
4
Always check for None before dereferencing next and random to avoid NullPointerException.
5
This problem tests deep vs shallow copy understanding and pointer manipulation.
INTERVIEW PREP · PRACTICE MODE

Interview Questions on This Topic

FAQ · 6 QUESTIONS

Frequently Asked Questions

01
What is the O(1) space approach?
02
Why can't you just copy next and random in one pass?
03
Does this algorithm work if random pointers form cycles?
04
How is this different from cloning a plain linked list?
05
Can Java's clone() method handle this?
06
How do you verify a clone is correct?
🔥

That's Linked List. Mark it forged?

3 min read · try the examples if you haven't

Previous
LRU Cache Implementation
10 / 10 · Linked List
Next
Stack Data Structure