150NeetCode 150

39.Copy List With Random Pointer

MediumLinked List ยท 5/11

Problem

A linked list of length n is given where each node has two pointers: next (the usual next node) and random, which can point to any node in the list or null.

Build a deep copy: exactly n brand-new nodes, where each copy has the same val as its original, and the copies' next and random pointers point to the corresponding copied nodes โ€” never to nodes of the original list. Return the head of the copied list.

Examples

Input:  head = [[7,null],[13,0],[11,4],[10,2],[1,0]]
Output: [[7,null],[13,0],[11,4],[10,2],[1,0]]
Explanation: Each pair is [val, index of random node]. The copy has the same shape,
but every node is a new object.
Input:  head = [[1,1],[2,1]]
Output: [[1,1],[2,1]]
Explanation: Both nodes' random pointers point to the second node.
Input:  head = [[3,null],[3,0],[3,null]]
Output: [[3,null],[3,0],[3,null]]
Explanation: Values are not unique, so you cannot match nodes by value.

Constraints

  • 0 <= n <= 1000
  • -10^4 <= Node.val <= 10^4
  • Node.random is null or points to some node in the linked list.

๐Ÿ’ก Key Insight

The hard part is the random pointer: when you copy a node, the node its random points to may not have been copied yet. The fix is to separate creating nodes from wiring them. A HashMap<original, copy> lets you look up any node's copy in O(1); the optimal trick stores that same mapping inside the list by placing each copy right after its original, so original.random.next is the copy of the random target.

Brute Force (Index Lookup)

TimeO(nยฒ)SpaceO(n)

Intuition

Copy the list following next only, keeping copies in an array. For each original node, find which index its random points to by scanning from the head, then point the copy at the copy with the same index.

Algorithm

  1. Walk the list, creating copies in copies[] and linking copies[i].next = copies[i + 1].
  2. For each original node i with non-null random:
    • Scan from head to find the index j of random.
    • Set copies[i].random = copies[j].
  3. Return copies[0] (or null).

Visualization

original: 7 -> 13 -> 11 -> 10 -> 1
13.random = 7  -> scan: 7 is index 0 -> copy13.random = copies[0]
11.random = 1  -> scan: 1 is index 4 -> copy11.random = copies[4]

Code

java
import java.util.ArrayList;
import java.util.List;

/*
// Definition for a Node.
class Node {
    int val;
    Node next;
    Node random;

    public Node(int val) {
        this.val = val;
        this.next = null;
        this.random = null;
    }
}
*/
class Solution {
    public Node copyRandomList(Node head) {
        if (head == null) {
            return null;
        }
        List<Node> copies = new ArrayList<>();
        for (Node curr = head; curr != null; curr = curr.next) {
            copies.add(new Node(curr.val));
        }
        for (int i = 0; i + 1 < copies.size(); i++) {
            copies.get(i).next = copies.get(i + 1);
        }
        int i = 0;
        for (Node curr = head; curr != null; curr = curr.next, i++) {
            if (curr.random == null) continue;
            // Find the index of the random target by scanning from head
            int j = 0;
            Node probe = head;
            while (probe != curr.random) {
                probe = probe.next;
                j++;
            }
            copies.get(i).random = copies.get(j);
        }
        return copies.get(0);
    }
}

Complexity

  • Time: O(nยฒ) โ€” each random pointer may require scanning the whole list.
  • Space: O(n) โ€” the array of copies (needed anyway for the output).

Complexity Comparison

Approach Time Space (extra)
Index Lookup O(nยฒ) O(n)
Hash Map O(n) O(n)
Interleaving Nodes O(n) O(1)

Common Pitfalls

  • Setting copy.random = orig.random โ€” that points the copy into the original list, which is not a deep copy.
  • Trying to wire random in the same pass as creating nodes, before the target's copy exists.
  • Matching nodes by value โ€” values can repeat, so you must map by node identity.
  • In the interleaving approach, forgetting to restore the original list's next pointers (LeetCode checks the input is unmodified).
  • Not checking curr.random != null before calling curr.random.next.