Linked Lists · Linked List Fundamentals

Linked List Deep Copy

Deep-copying a list whose nodes carry an extra pointer to an arbitrary other node, by first mapping every original node to a new clone, then wiring each clone's pointers using that map.

  • 6 min read
  • 12 practice questions
  • 1 graded problem
On this page
  1. Description
  2. The approach
  3. A second flavor: interleaving clones into the original list
  4. Where you'll see it
  5. Which tool for which job?
  6. Common mistakes
  7. Check yourself
  8. Key takeaways
  9. Quick revise
  10. Practice

Description

Some linked lists give each node an extra random pointer to any node in the list. Deep-copying one means every pointer in the copy must point at another copy, never back into the original. The standard answer keeps a map from each original node to its clone. Create all the clones first, then wire next and random through the map. A space-saving alternative weaves each clone in right after its original, then separates the two lists.

We'll go from simple to more technical:

  1. The pattern: original-to-clone map, two passes.
  2. A second flavor: interleave the clones, wire them, then un-interleave.
  3. Where this is the general recipe for copying object graphs.
  4. The wiring bugs, and when the extra map memory matters.

The approach

Copying a plain list is one walk. The random pointer is what makes this hard. To wire a clone's random, the clone of its target must already exist, and the target can be anywhere, even further down the list. So do it in two passes. First create every clone, unwired, and remember each one in a map from original to clone. Then walk again and wire each clone's next and random by looking their targets up in the map.

Take 7 -> 3 -> 5, where 7's random points ahead to 5 and 3's points back to 7:

next

next

random

random

7

3

5

next

next

random

random

7

3

5

PassAt nodeWhat happens
1each nodeclones 7', 3' and 5' are made; the map is {7: 7', 3: 3', 5: 5'}
277'.next = map[3] = 3', and 7'.random = map[5] = 5'
233'.next = 5', and 3'.random = map[7] = 7'
255'.next = map[null] = null, and 5'.random = null

Wiring 7' in the first pass would have failed: 5' didn't exist yet.

Java

Map<Node, Node> map = new HashMap<>();
Node cur = head;
while (cur != null) {                        // pass 1: create every clone, unwired
    map.put(cur, new Node(cur.val));
    cur = cur.next;
}
cur = head;
while (cur != null) {                         // pass 2: wire the clones using the map
    map.get(cur).next = map.get(cur.next);     // map.get(null) is null - handles the tail cleanly
    map.get(cur).random = map.get(cur.random);
    cur = cur.next;
}
return map.get(head);

Python

def copy_random_list(head: Node | None) -> Node | None:
    clone = {}
    cur = head
    while cur is not None:                   # pass 1: create every clone, unwired
        clone[cur] = Node(cur.val)
        cur = cur.next
    cur = head
    while cur is not None:                   # pass 2: wire the clones using the dict
        clone[cur].next = clone.get(cur.next)      # .get(None) is None - handles the tail cleanly
        clone[cur].random = clone.get(cur.random)
        cur = cur.next
    return clone.get(head)

Looking up null returns null, so the tail and missing random pointers need no special case.

A second flavor: interleaving clones into the original list

The map costs O(n) extra memory. You can avoid it by placing each clone right after its original: 7 -> 7' -> 3 -> 3' -> 5 -> 5'. Then any node's clone is simply its next, and that layout does the map's job:

Pass 1: after every original node, insert its clone:   7 -> 7' -> 3 -> 3' -> 5 -> 5'
Pass 2: for every original X with a random pointer, set X'.random = X.random.next
        (the clone of X.random sits right after it)
Pass 3: walk the chain again and unthread it: the originals link up as before,
        and the clones form their own list

For 7, 7.random is 5, and 5.next is 5', exactly the clone 7' should point to. The price is the third pass. It must restore the original list's next pointers as well as pull out the clones, and a slip there damages the input, which the map version never touches.

Where you'll see it

This is the general recipe for deep-copying any object graph with cross-references, cycles or shared objects. Keep a map from original to copy, and fill it in before following references. Python's copy.deepcopy does exactly that with its memo dictionary: it records each copy before copying its fields.

Which tool for which job?

You needUse
Copy a list with random pointers, simplyan original→clone map, two passes
The same with O(1) extra spaceinterleave clones, wire random, un-interleave
Copy any object graph (cycles, shared nodes)the same map, filled before following references
Nodes that override equals/hashCode (Java)IdentityHashMap
Python nodes that define __eq__key the map by id(node)
A quick copy in Pythoncopy.deepcopy - for short structures only

Common mistakes

MistakeWhat happensDo this instead
Wiring random while still creating clonesforward targets aren't cloned yetcreate every clone first
Pointing a clone's random at an originalthe copy shares nodes with the inputalways look the target up in the map
A value-equality map keyequal nodes share one clonean identity map
Skipping the un-interleave passthe input stays threaded with clonesseparate both lists
copy.deepcopy on a long listRecursionErrorthe iterative map version

Check yourself

Try to answer before opening each one - recalling beats re-reading.

1. Why can't a clone's random be wired in the same pass that creates the clones?

Show answer

random can point to a node further down the list, which hasn't been cloned yet. In the example, 7 points to 5. A single forward pass would look up a clone that doesn't exist yet.

2. map.get(cur.next) runs even at the tail, where cur.next is null. Why no null check?

Show answer

Null was never put in the map, so looking it up returns null. That's exactly what the tail's clone should have as its next. The map already does the right thing.

3. In the interleaved version, why is cur.random.next the clone of cur.random?

Show answer

Pass 1 put every original's clone right after it. cur.random is an original node, so its clone sits at its next. The layout itself does the map's job.

Key takeaways

  • Create every clone first, then wire them. A random target's clone must exist before you wire to it.
  • Looking up null in the map returns null, which handles the tail and missing pointers.
  • Interleaving each clone after its original makes "find the clone" just .next, with no map, but needs a careful third pass to separate the lists.
  • Both are O(n) time. The trade is O(n) extra space and simple code, against O(1) space and trickier pointer work.

Quick revise

The whole skill on one page, for the night before an interview.

Everything from the lesson and the deep dive on one page. Skim it before practice or an interview, and open the full lesson for anything that doesn't ring a bell.

Pick the tool

You needUse
Simple and safeoriginal→clone map, two passes
O(1) extra spaceinterleave, wire, un-interleave
Any graphmap + BFS/DFS
Nodes with value equalityIdentityHashMap / id()
Short structures in Pythoncopy.deepcopy

Snippets worth knowing by heart

Java

Map<Node, Node> clone = new IdentityHashMap<>();             // identity, not equals
for (Node n = head; n != null; n = n.next) clone.put(n, new Node(n.val));
for (Node n = head; n != null; n = n.next) {
    clone.get(n).next = clone.get(n.next);                   // get(null) is null
    clone.get(n).random = clone.get(n.random);
}
return clone.get(head);

Python

clone = {}
node = head
while node:
    clone[id(node)] = Node(node.val)          # id(): identity, even if Node defines __eq__
    node = node.next
node = head
while node:
    c = clone[id(node)]
    c.next = clone[id(node.next)] if node.next else None
    c.random = clone[id(node.random)] if node.random else None
    node = node.next
return clone[id(head)] if head else None

The traps

  • Wiring during creation: forward random targets aren't cloned yet. Create every clone first.
  • Pointing at originals: the copy shares nodes with the input. Look every target up in the map.
  • Value-equality keys: equal nodes share one clone. Use an identity map.
  • No un-interleave: the input stays threaded with clones. Separate both lists.
  • deepcopy on long lists: it recurses and raises RecursionError. Use the iterative map version.

Staff-level points, one line each

  • A value-equality HashMap made 2 clones for 3 nodes; IdentityHashMap made 3.
  • A default @dataclass node is unhashable - key by id().
  • copy.deepcopy keeps random links and cycles via its memo, but recursion fails at 5,000 nodes.
  • General graphs: register the clone before following references.
  • Interleaving mutates the input until the last pass.

Say it in an interview

  • "I map every original node to its clone, then wire pointers through the map."
  • "Two passes, because a random pointer can point forward."
  • "The map compares by identity."
  • "For O(1) extra space I'd interleave the clones and separate them at the end."

Practice

This skill comes with 12 multiple-choice questions that tell you which misconception a wrong answer reveals and 1 graded problem. Your answers are graded and your mastery is tracked, so you'll know when the skill holds up. Running code and saving progress need a free account.

Practice Linked List Deep Copy free

Copy List with Random Pointer

MediumProCoding problemJava · Python

Each node of this linked list has two pointers: the usual next, and random, which may point at any node in the list or at nothing. Build a deep copy: a list of brand-new nodes whose next and random pointers mirror the original's, without any of them pointing back into the…

The full statement, the examples and the hidden test suite are part of AI Proof Engineers Pro.

Practice Linked List Deep Copy free

No card needed. Lessons, practice questions and easy problems are free forever.

Free plan, no card · 7-day Pro trial · Cancel anytime