Graphs · Graph Traversal Fundamentals

Graph Clone

Cloning a graph reachable from a given node by traversing it once (BFS or DFS) while maintaining a map from each original node to its already-created copy, so that a node's clone is created exactly once no matter how many other nodes point back to it.

  • 5 min read
  • 8 practice questions
  • 2 graded problems
On this page
  1. Description
  2. The approach
  3. A second flavor: breadth-first cloning
  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

A deep copy of a graph has exactly one new node for each original node, the same connections between them, and no references back into the original. Copying a tree is simple recursion. A graph adds two complications: a node reached along two paths must still be copied only once, and a cycle leads back to a node whose copy isn't finished yet. A map from each original node to its copy solves both.

Copying a tree is easy: copy the node, recursively copy its children. A graph breaks that in two ways. A node can be reached along several paths (shared neighbors), so a naive copy duplicates it; and a cycle leads back to a node that's still being copied, so a naive copy never finishes. A deep copy of a graph has to produce exactly one new node per original node, with the same connections - and never point into the original.

We'll go from simple to more technical:

  1. The pattern: a DFS clone that registers each copy before cloning its neighbors.
  2. A second flavor: cloning breadth-first with a queue.
  3. Where identity maps run in production (deep copies, serialization).
  4. What breaks it: registering late, no map, keying by value, and shallow neighbors.

The approach

Java

Node cloneGraph(Node node) {
    if (node == null) return null;
    return clone(node, new HashMap<>());
}

Node clone(Node node, Map<Node, Node> copies) {
    if (copies.containsKey(node)) return copies.get(node);   // already copied (or being copied) - reuse it
    Node copy = new Node(node.val);
    copies.put(node, copy);                                   // register BEFORE visiting neighbors
    for (Node neighbor : node.neighbors) {
        copy.neighbors.add(clone(neighbor, copies));
    }
    return copy;
}

Python

def clone_graph(node: Node | None) -> Node | None:
    if node is None:
        return None
    copies = {}                                  # original node -> its copy

    def clone(n: Node) -> Node:
        if n in copies:
            return copies[n]                     # already copied (or being copied) - reuse it
        copy = Node(n.val)
        copies[n] = copy                         # register BEFORE visiting neighbors
        for neighbor in n.neighbors:
            copy.neighbors.append(clone(neighbor))
        return copy

    return clone(node)

The map from original node to copy does both jobs. A node reached a second time along another path gets its existing copy, so it's never duplicated. And because the copy is registered before its neighbors are cloned, a cycle that leads back to it finds the half-built copy in the map instead of starting another one - which is what makes the recursion stop.

A second flavor: breadth-first cloning

The recursion is as deep as the longest path it follows. A queue does the same job iteratively: create a node's copy the first time it's seen, and wire up edges as each node is processed.

Java

Node cloneGraph(Node start) {
    if (start == null) return null;
    Map<Node, Node> copies = new HashMap<>();
    copies.put(start, new Node(start.val));
    Deque<Node> queue = new ArrayDeque<>(List.of(start));
    while (!queue.isEmpty()) {
        Node cur = queue.poll();
        for (Node next : cur.neighbors) {
            if (!copies.containsKey(next)) {          // first sighting: create its copy and schedule it
                copies.put(next, new Node(next.val));
                queue.add(next);
            }
            copies.get(cur).neighbors.add(copies.get(next));
        }
    }
    return copies.get(start);
}

Python

from collections import deque


def clone_graph(start: Node | None) -> Node | None:
    if start is None:
        return None
    copies = {start: Node(start.val)}
    queue = deque([start])
    while queue:
        cur = queue.popleft()
        for nxt in cur.neighbors:
            if nxt not in copies:                # first sighting: create its copy and schedule it
                copies[nxt] = Node(nxt.val)
                queue.append(nxt)
            copies[cur].neighbors.append(copies[nxt])
    return copies[start]

The map is the BFS visited set: a node's copy is created exactly once, at discovery, which is also the moment it's queued. The copy's neighbor lists keep the original order because each node's edges are added while that node is processed.

Where you'll see it

Deep-copying any structure with shared or circular references - an object graph being snapshotted, a document model where nodes point to each other, a workflow definition duplicated for a new tenant - is this "original -> copy" map. Java serialization, Python's copy.deepcopy (its memo dict is keyed by id()) and most deep-clone libraries keep exactly such an identity map to preserve sharing and survive cycles.

Which tool for which job?

You needUse
A deep copy of a small graphDFS with an original-to-copy map
A deep copy of a large or deep graphBFS with the same map
Keys that mean "this exact object"IdentityHashMap in Java; identity-hashed objects or id() in Python
A quick copy of a small object graph in Pythoncopy.deepcopy (it keeps a memo, but recurses)
Copies that are rarely changedcopy-on-write instead of copying everything up front

Common mistakes

MistakeWhat happensDo this instead
Registering the copy after cloning neighborsinfinite recursion on any cycleregister first, then recurse
No map at allshared nodes get duplicated (a diamond becomes 5 nodes)always look up the map first
Keying the map by valuenodes with equal values collapse into one copykey by identity
Copying neighbor referencesthe clone points into the original graphadd the neighbors' copies
Recursion or deepcopy on a deep graphstack overflow or RecursionErrora queue-based clone

Check yourself

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

1. The copy is put into the map only after all its neighbors have been cloned. What happens on a graph where 1 and 2 are neighbors of each other?

Show answer

Infinite recursion. Cloning 1 starts cloning 2; cloning 2 looks up 1 in the map - not there yet - and starts cloning 1 again, forever, until the stack overflows. Registering the copy before visiting the neighbors lets the second lookup find it.

2. The graph has no cycles: A->B, A->C, B->D, C->D. Does a clone without any map work?

Show answer

It terminates, but it's wrong: D is reached through both B and C and gets copied twice, so the clone has 5 nodes where the original has 4, and B's and C's copies point to different D's. The map is needed for sharing, not only for cycles.

3. Why key the map by the node rather than by its value?

Show answer

Values can repeat. With a value-keyed map, the second node holding value 5 finds the first node's copy and reuses it - two originals collapse into one copy. Keying by the node (its identity) gives every original its own copy.

Key takeaways

  • A graph deep copy keeps a map from each original node to its single copy and checks it before creating anything.
  • Register the copy before cloning its neighbors - that's what stops the recursion on cycles.
  • The map also preserves sharing: a node reached along two paths still gets one copy.
  • Key by node identity, not by value - values can repeat.
  • DFS recursion or BFS with a queue both work in O(V + E); BFS avoids deep recursion.

Quick revise

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

This page puts the lesson and the deep dive together. Skim it before practice or an interview, and go back to the full lesson for anything you don't recognize.

Pick the tool

You needUse
Deep copy, small graphDFS plus a map
Deep or large graphBFS plus a map
Java map keyIdentityHashMap
Rarely modified copiescopy-on-write

Snippets worth knowing by heart

Java

Map<Node, Node> copies = new IdentityHashMap<>();
copies.put(start, new Node(start.val));
Deque<Node> q = new ArrayDeque<>(List.of(start));
while (!q.isEmpty()) {
    Node cur = q.poll();
    for (Node nx : cur.neighbors) {
        if (!copies.containsKey(nx)) { copies.put(nx, new Node(nx.val)); q.add(nx); }
        copies.get(cur).neighbors.add(copies.get(nx));
    }
}

Python

def clone(n):
    if n in copies:
        return copies[n]
    copy = Node(n.val)
    copies[n] = copy                     # register BEFORE the neighbors
    copy.neighbors = [clone(x) for x in n.neighbors]
    return copy

The traps

  • Registering late: cloning neighbors before registering the copy recurses forever on a cycle. Register first, then recurse.
  • No map: shared nodes get duplicated, so a diamond becomes five nodes. Look up the map first.
  • Keying by value: nodes with equal values collapse into one copy. Key by identity (IdentityHashMap, id()).
  • Shallow neighbors: the clone points back into the original graph. Add the neighbors' copies.
  • Recursive deep copies on deep graphs: the stack overflows. Clone with a queue.

Staff-level points, one line each

  • Proof of a clone: the same shape and 0 shared nodes (checked on 1,000 nodes and 3,999 edges).
  • HashMap with value equals cloned 3 nodes into 2; IdentityHashMap gave 3.
  • A Python class with __eq__ and no __hash__ can't be a dict key (TypeError).
  • copy.deepcopy copied a 100-node cyclic graph, but raised RecursionError on a 1,000-node graph and a 10,000-node chain.
  • Java serialization overflowed the stack on a 100,000-node chain; the queue clone copied it.

Say it in an interview

  • "I keep a map from each original to its copy and check it before creating anything."
  • "I register the copy before visiting neighbors, so cycles terminate."
  • "The map is keyed by identity, not value."
  • "For deep graphs I'd clone with a queue."

Practice

This skill comes with 8 multiple-choice questions that tell you which misconception a wrong answer reveals and 2 graded problems. 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 Graph Clone free

Clone Graph

MediumProCoding problemJava · Python

Given a reference to a node in a connected undirected graph (nodes valued 1 to n), return a deep copy (clone) of the entire graph. Every node in the clone must be a new node; a clone that reaches any node of the original graph is rejected.

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

Clone Graph With Duplicate Values

MediumProCoding problemJava · Python

Given a reference to a node in a connected undirected graph, return a deep copy of the entire graph. Unlike the basic version, node values are not unique - several nodes can hold the same value.

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

Practice Graph Clone free

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

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