Each node has a next pointer and a random pointer that can point anywhere in the list (or null). Deep-copy the list — a hash map from original to clone lets you resolve every pointer in two linear passes.
Given a linked list where every node has a val, a next pointer, and a random pointer (which can point to any node in the list or be null), return a fully independent deep copy of the list.
Concrete example: [7→null, 13→0, 11→4, 10→2, 1→0] where each entry is val→random_index. After copying, the new list has the same structure and the same random targets (by position), but no node is shared with the original.
clone.random while creating nodes, because the target clone might not exist yet. The fix is to separate creation from wiring: one pass builds a clone for every node and stores original → clone in a map. A second pass wires clone.next and clone.random by looking up each target in the map.head is null, return null immediately.{ val, next: null, random: null } and store it in map.set(original, clone). Skip wiring — you only need shallow copies right now.clone.next = map.get(cur.next) ?? null and clone.random = map.get(cur.random) ?? null. The map guarantees the targets already exist.map.get(head)is the new list's head.randomin a single pass. If node A's randompoints to node C and you haven't cloned C yet, you have nothing to wire to. You must create all clones first, then do the wiring in a second pass (or use the interleave trick below).The interleave trick achieves O(1) extra space. Pass 1: insert each clone immediately after its original (A→A'→B→B'→…). Pass 2: set clone.random = cur.random.next (the interleaved clone). Pass 3: separate the two lists. More code to write; O(n) space is almost always acceptable in interviews.
1interface Node {2 val: number;3 next: Node | null;4 random: Node | null;5}67function copyRandomList(head: Node | null): Node | null {8 if (!head) return null;910 // Pass 1: build a clone for every original node.11▶ const map = new Map<Node, Node>();12▶ let cur: Node | null = head;13 while (cur) {14 map.set(cur, { val: cur.val, next: null, random: null });15 cur = cur.next;16 }1718 // Pass 2: wire next and random using the map.19 cur = head;20 while (cur) {21 const clone = map.get(cur)!;22 clone.next = cur.next ? map.get(cur.next)! : null;23 clone.random = cur.random ? map.get(cur.random)! : null;24 cur = cur.next;25 }2627 return map.get(head)!;28}
interface Node {
val: number;
next: Node | null;
random: Node | null;
}
function copyRandomList(head: Node | null): Node | null {
if (!head) return null;
// Pass 1: build a clone for every original node.
const map = new Map<Node, Node>();
let cur: Node | null = head;
while (cur) {
map.set(cur, { val: cur.val, next: null, random: null });
cur = cur.next;
}
// Pass 2: wire next and random using the map.
cur = head;
while (cur) {
const clone = map.get(cur)!;
clone.next = cur.next ? map.get(cur.next)! : null;
clone.random = cur.random ? map.get(cur.random)! : null;
cur = cur.next;
}
return map.get(head)!;
}null; avoids special-casing the map lookup at the end.val and both pointers set to nullfor now. No wiring happens here — we're just building the lookup table so every target exists before we need it.cur, look up its clone and set clone.next and clone.random via the map. The optional-chain guard (cur.next ? map.get(cur.next)! : null) handles tail and null random pointers cleanly. The non-null assertion (!) is safe because Pass 1 guarantees every original has a clone.map.get(head)! is the entry point into the fully wired copy.A→A'→B→B'→…), set clone.random = original.random.next, then split the two lists apart.map.get returns the already-created clone instead of looping infinitely.null), single node with random pointing to itself, all random pointers null.interface Node {
val: number;
next: Node | null;
random: Node | null;
}
function copyRandomList(head: Node | null): Node | null {
if (!head) return null;
// Pass 1: build a clone for every original node.
const map = new Map<Node, Node>();
let cur: Node | null = head;
while (cur) {
map.set(cur, { val: cur.val, next: null, random: null });
cur = cur.next;
}
// Pass 2: wire next and random using the map.
cur = head;
while (cur) {
const clone = map.get(cur)!;
clone.next = cur.next ? map.get(cur.next)! : null;
clone.random = cur.random ? map.get(cur.random)! : null;
cur = cur.next;
}
return map.get(head)!;
}Build clones into parallel arrays in one pass, then resolve each next and randomby walking the original list from the head to find the target node's position — a linear scan for every pointer.
interface Node {
val: number;
next: Node | null;
random: Node | null;
}
function copyRandomList(head: Node | null): Node | null {
if (!head) return null;
// Collect originals and make a bare clone for each.
const originals: Node[] = [];
for (let cur: Node | null = head; cur; cur = cur.next) originals.push(cur);
const clones: Node[] = originals.map((o) => ({ val: o.val, next: null, random: null }));
// Helper: position of a node in the original list (linear search).
const indexOf = (target: Node | null): number =>
target ? originals.indexOf(target) : -1;
for (let i = 0; i < originals.length; i++) {
const ni = indexOf(originals[i].next);
const ri = indexOf(originals[i].random);
clones[i].next = ni === -1 ? null : clones[ni];
clones[i].random = ri === -1 ? null : clones[ri];
}
return clones[0];
}indexOf rescans the list, so resolving all pointers is O(n²). Caching the original to clone correspondence in a Map turns every lookup into O(1), giving the two-pass O(n) optimal.| deep-copy a list/graph with cross-references | original→clone hash map |
| pointer target may not exist yet at assignment time | two-pass: create then wire |
| O(1) space deep copy of a linked list | interleave clones A→A'→B→B'→… |
| "clone graph" or "copy with random pointer" | Map + two passes (or BFS/DFS) |
const map = new Map<Node, Node>();
// Pass 1: clone every node (no wiring yet)
let cur: Node | null = head;
while (cur) {
map.set(cur, { val: cur.val, next: null, random: null });
cur = cur.next;
}
// Pass 2: wire next and random
cur = head;
while (cur) {
const clone = map.get(cur)!;
clone.next = cur.next ? map.get(cur.next)! : null;
clone.random = cur.random ? map.get(cur.random)! : null;
cur = cur.next;
}
return map.get(head)!;[7→null, 13→0] (val→random_index). After the algorithm, what is clone[1].random.val?