Algorithm

Copy List with Random Pointer

Linked List Pattern

Copy List with Random Pointer

Each node of a singly linked list has the usual next pointer plus a second random pointer that points to any node in the list or to null. Build a deep copy: a brand-new list of new nodes whose next and random pointers mirror the original's structure exactly, but point only among the new nodes — no new node may reference any original node. Return the head of the copied list. The original list must be left unmodified.

CONSTRAINTS
  • The number of nodes is in the range [0, 1000]
  • -10⁴ <= Node.val <= 10⁴
  • random points to a node in the list, or is null
EXAMPLE 1
Input: head = [[7,null],[13,0],[11,4],[10,2],[1,0]]
Output: [[7,null],[13,0],[11,4],[10,2],[1,0]]
Each pair is [value, index-that-random-points-to]. The copy has the same values and the same random targets by position (e.g. node 13's random points to index 0, the node holding 7), but every node and pointer belongs to the new list.
EXAMPLE 2
Input: head = [[1,1],[2,1]]
Output: [[1,1],[2,1]]
Both nodes' random pointers target index 1 (the second node). The clone reproduces that: both cloned randoms point to the cloned second node, never to an original.
EXAMPLE 3
Input: head = []
Output: []
An empty list has nothing to copy, so the deep copy is also empty (null).
What is a deep copy here, versus a shallow one?
A deep copy creates entirely new nodes whose next and random pointers reference only the new nodes. A shallow copy would reuse or point back at the original nodes — which is exactly what the problem forbids.
Can a random pointer be null or point to the node itself?
Yes to both. random may be null, and it may point to any node including its own. Mapping null to null and treating self-references like any other target handles these uniformly.
Must the original list be left unchanged?
Yes. The map-based method never touches it; the O(1)-space interleave method temporarily mutates it, so its final pass must fully restore the original next pointers before returning.
Do multiple nodes with the same value cause ambiguity?
No — pointers reference specific node objects, not values, so identical values never confuse which node a random points to.

A deep copy means new nodes all the way down: not just copied values, but copied links — every next and every random in the clone must point to a clone, never back into the original. The plain next pointers are easy (walk and copy in order). The random pointers are the whole difficulty.

Why random pointers are a chicken-and-egg problem

Copying next is easy because it goes forward: when you're building clone of node A and need A.next's clone, you can just create it now. But A.random can point anywhere — often to a node further down the list that you haven't reached yet, so its clone doesn't exist to point at. You cannot set cloneA.random until clone(A.random) has been created, and you can't guarantee that ordering while walking once. The core need is a way to answer, for any original node, "what is your clone?" — regardless of where it sits.

The clean answer: a map from original to clone

Give each original node an entry in a hash map: original → its clone. Do two passes. First pass: walk the list and create a bare clone (value only) for every node, recording the pairing in the map. Now every clone exists. Second pass: walk again and wire the clones using the map — clone.next = map[original.next], clone.random = map[original.random]. Because the map already holds a clone for every original (and we map null to null), the random target always exists, no matter how far it jumps. Clear and correct, at the price of O(N) extra space for the map.

python
if not head: return None
clones = {None: None}          # map None -> None so null links copy cleanly

curr = head                    # pass 1: make every clone, record the pairing
while curr:
    clones[curr] = Node(curr.val)
    curr = curr.next

curr = head                    # pass 2: wire next and random via the map
while curr:
    clones[curr].next = clones[curr.next]
    clones[curr].random = clones[curr.random]
    curr = curr.next
return clones[head]
Removing the map: store each clone next to its original

The map only exists to answer "where is X's clone?" We can make that answer free by putting each clone immediately after its original in the list itself, so a node's clone is always node.next. Three passes:
- Interleave: after each original node X, splice in its clone X', giving X → X' → Y → Y' → … Now clone(X) is literally X.next.
- Wire randoms: for each original X, its clone is X.next and the clone of X.random is X.random.next. So X.next.random = X.random.next — the map lookup becomes a one-hop pointer step.
- Detach: unweave the two lists, restoring every original's next and stitching the clones together into their own list.

python
if not head: return None

curr = head                    # 1. interleave clones into the original
while curr:
    clone = Node(curr.val)
    clone.next = curr.next
    curr.next = clone
    curr = clone.next

curr = head                    # 2. wire clone randoms from neighbors
while curr:
    if curr.random:
        curr.next.random = curr.random.next
    curr = curr.next.next

curr = head                    # 3. split original and clone back apart
clone_head = head.next
while curr:
    clone = curr.next
    curr.next = clone.next            # restore original link
    clone.next = clone.next.next if clone.next else None
    curr = curr.next
return clone_head
Crucial Notetwo things bite here. First, guard if curr.random before curr.random.next — a null random has no .next, and the interleave step depends on cloneX = X.next, so X.random.next is genuinely the clone of X.random only while the lists are still interwoven, which is why random-wiring must happen before detaching. Second, pass 3 must restore the original list as it separates the clones; leaving the two interleaved (or leaving originals pointing at clones) mutates the input, which the problem forbids. Detaching is not cleanup you can skip — it is what makes both lists well-formed.
Worked Example:7 → 13, with 13.random = 7 (and 7.random = null)
- Interleave: 7 → 7' → 13 → 13'. Each clone sits right after its original.
- Wire randoms: 7.random is null, skip. 13.random is 7, so 13'.random = 7.random.next = 7' — the clone, exactly right.
- Detach: restore 7 → 13 (original intact) and split off 7' → 13' (13'.random points at 7').
- Result: a copy 7' → 13' living in new memory, with 13' randomly pointing to 7', and the original untouched.
The trade, and the reflex

Both solutions are O(N) time; the map version costs O(N) space, the interleave version O(1) extra (ignoring the output the problem requires). The transferable idea is the interleave trick: when you need a lookup from old thing to new thing, you can sometimes store the new thing physically adjacent to the old one and replace the map with a pointer hop. It's a niche move, but it captures the general reflex — an O(N)-space association can occasionally be encoded into the structure you already have, if you can weave the copy in and cleanly unweave it afterward.

Interactive Strategy Visualization
DEEP CLONE ENGINE

3-Pass In-Place Algorithm

7
7'
13
13'
11
11'
INTERLEAVING

Phase Details

Phase 1: Interleaving

Create copy nodes and interleave them with the original: A -> A' -> B -> B' -> C -> C'. Note: Original random pointers exist but are not yet copied.

Memory Insight

Interleaving copy nodes inside the original list eliminates the need for a Hash Map (O(N) space). We trade O(1) space for O(N) time.

Strategy: Constant Space Deep Copy

Time Complexity: O(N) | Space Complexity: O(1) extra space. The most elegant solution for cloning with random pointers.

O(N) Time · O(N) Space Hash Map
O(N) Time · O(1) Space Interleave & Detach