LeetCode 138: Copy List with Random Pointer (Node Splitting + Rewire)
LeetCode 138Linked ListRandom PointerToday we solve LeetCode 138 - Copy List with Random Pointer.
Source: https://leetcode.com/problems/copy-list-with-random-pointer/
English
Problem Summary
Given a linked list where each node has a next pointer and a random pointer (which can point to any node or null), return a deep copy of the list.
Key Insight
The O(1)-extra-space trick is to interleave cloned nodes directly inside the original list: A -> A' -> B -> B' .... Then each clone's random can be set by clone.random = original.random.next.
Brute Force and Limitations
Brute force with a hash map (old -> new) is straightforward and valid, but uses O(n) extra memory. We can do better in auxiliary space with interleaving.
Optimal Algorithm Steps (Interleaving)
1) First pass: insert clone node after every original node.
2) Second pass: wire clone random pointers using curr.next.random = curr.random == null ? null : curr.random.next.
3) Third pass: detach the mixed list into original list and cloned list.
Complexity Analysis
Time: O(n).
Extra space: O(1) (excluding output list).
Common Pitfalls
- Forgetting to restore original next links while detaching.
- Using clone.random = curr.random (wrong object graph).
- Failing null checks for random pointer.
- Advancing pointers incorrectly and skipping nodes in pass 2/3.
Reference Implementations (Java / Go / C++ / Python / JavaScript)
/*
// 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;
Node curr = head;
while (curr != null) {
Node clone = new Node(curr.val);
clone.next = curr.next;
curr.next = clone;
curr = clone.next;
}
curr = head;
while (curr != null) {
Node clone = curr.next;
clone.random = (curr.random == null) ? null : curr.random.next;
curr = clone.next;
}
Node dummy = new Node(0);
Node tail = dummy;
curr = head;
while (curr != null) {
Node clone = curr.next;
curr.next = clone.next;
tail.next = clone;
tail = clone;
curr = curr.next;
}
return dummy.next;
}
}/**
* Definition for a Node.
* type Node struct {
* Val int
* Next *Node
* Random *Node
* }
*/
func copyRandomList(head *Node) *Node {
if head == nil {
return nil
}
for curr := head; curr != nil; {
clone := &Node{Val: curr.Val}
clone.Next = curr.Next
curr.Next = clone
curr = clone.Next
}
for curr := head; curr != nil; curr = curr.Next.Next {
clone := curr.Next
if curr.Random != nil {
clone.Random = curr.Random.Next
}
}
dummy := &Node{}
tail := dummy
for curr := head; curr != nil; {
clone := curr.Next
curr.Next = clone.Next
tail.Next = clone
tail = clone
curr = curr.Next
}
return dummy.Next
}/*
// Definition for a Node.
class Node {
public:
int val;
Node* next;
Node* random;
Node(int _val) {
val = _val;
next = NULL;
random = NULL;
}
};
*/
class Solution {
public:
Node* copyRandomList(Node* head) {
if (!head) return nullptr;
Node* curr = head;
while (curr) {
Node* clone = new Node(curr->val);
clone->next = curr->next;
curr->next = clone;
curr = clone->next;
}
curr = head;
while (curr) {
Node* clone = curr->next;
clone->random = curr->random ? curr->random->next : nullptr;
curr = clone->next;
}
Node dummy(0);
Node* tail = &dummy;
curr = head;
while (curr) {
Node* clone = curr->next;
curr->next = clone->next;
tail->next = clone;
tail = clone;
curr = curr->next;
}
return dummy.next;
}
};# Definition for a Node.
# class Node:
# def __init__(self, x: int, next: 'Node' = None, random: 'Node' = None):
# self.val = int(x)
# self.next = next
# self.random = random
class Solution:
def copyRandomList(self, head: 'Optional[Node]') -> 'Optional[Node]':
if not head:
return None
curr = head
while curr:
clone = Node(curr.val, curr.next, None)
curr.next = clone
curr = clone.next
curr = head
while curr:
clone = curr.next
clone.random = curr.random.next if curr.random else None
curr = clone.next
dummy = Node(0)
tail = dummy
curr = head
while curr:
clone = curr.next
curr.next = clone.next
tail.next = clone
tail = clone
curr = curr.next
return dummy.next/**
* // Definition for a Node.
* function Node(val, next, random) {
* this.val = val;
* this.next = next;
* this.random = random;
* }
*/
/**
* @param {Node} head
* @return {Node}
*/
var copyRandomList = function(head) {
if (!head) return null;
let curr = head;
while (curr) {
const clone = new Node(curr.val, curr.next, null);
curr.next = clone;
curr = clone.next;
}
curr = head;
while (curr) {
const clone = curr.next;
clone.random = curr.random ? curr.random.next : null;
curr = clone.next;
}
const dummy = new Node(0, null, null);
let tail = dummy;
curr = head;
while (curr) {
const clone = curr.next;
curr.next = clone.next;
tail.next = clone;
tail = clone;
curr = curr.next;
}
return dummy.next;
};中文
题目概述
给定一个链表,每个节点除了 next 还有一个可指向任意节点(或 null)的 random 指针。要求返回这个链表的深拷贝。
核心思路
最优空间做法是“节点穿插”:把克隆节点插到原节点后面,形成 A -> A' -> B -> B' ...。这样克隆节点的 random 可通过 原节点.random.next 直接定位。
基线解法与不足
哈希表映射(原节点 -> 新节点)写起来直观,时间 O(n)、额外空间 O(n)。但它不是辅助空间最优。
最优算法步骤(节点穿插)
1)第一轮:每个原节点后插入对应克隆节点。
2)第二轮:设置克隆节点 random,规则是 clone.random = curr.random == null ? null : curr.random.next。
3)第三轮:拆分链表,恢复原链表并抽出独立的克隆链表。
复杂度分析
时间复杂度:O(n)。
额外空间复杂度:O(1)(不计输出链表)。
常见陷阱
- 拆分时忘记把原链表 next 恢复。
- 把 clone.random 直接指向旧节点而不是新节点。
- 没处理 random 为 null 的情况。
- 第二轮和第三轮指针推进错误导致漏节点。
多语言参考实现(Java / Go / C++ / Python / JavaScript)
/*
// 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;
Node curr = head;
while (curr != null) {
Node clone = new Node(curr.val);
clone.next = curr.next;
curr.next = clone;
curr = clone.next;
}
curr = head;
while (curr != null) {
Node clone = curr.next;
clone.random = (curr.random == null) ? null : curr.random.next;
curr = clone.next;
}
Node dummy = new Node(0);
Node tail = dummy;
curr = head;
while (curr != null) {
Node clone = curr.next;
curr.next = clone.next;
tail.next = clone;
tail = clone;
curr = curr.next;
}
return dummy.next;
}
}/**
* Definition for a Node.
* type Node struct {
* Val int
* Next *Node
* Random *Node
* }
*/
func copyRandomList(head *Node) *Node {
if head == nil {
return nil
}
for curr := head; curr != nil; {
clone := &Node{Val: curr.Val}
clone.Next = curr.Next
curr.Next = clone
curr = clone.Next
}
for curr := head; curr != nil; curr = curr.Next.Next {
clone := curr.Next
if curr.Random != nil {
clone.Random = curr.Random.Next
}
}
dummy := &Node{}
tail := dummy
for curr := head; curr != nil; {
clone := curr.Next
curr.Next = clone.Next
tail.Next = clone
tail = clone
curr = curr.Next
}
return dummy.Next
}/*
// Definition for a Node.
class Node {
public:
int val;
Node* next;
Node* random;
Node(int _val) {
val = _val;
next = NULL;
random = NULL;
}
};
*/
class Solution {
public:
Node* copyRandomList(Node* head) {
if (!head) return nullptr;
Node* curr = head;
while (curr) {
Node* clone = new Node(curr->val);
clone->next = curr->next;
curr->next = clone;
curr = clone->next;
}
curr = head;
while (curr) {
Node* clone = curr->next;
clone->random = curr->random ? curr->random->next : nullptr;
curr = clone->next;
}
Node dummy(0);
Node* tail = &dummy;
curr = head;
while (curr) {
Node* clone = curr->next;
curr->next = clone->next;
tail->next = clone;
tail = clone;
curr = curr->next;
}
return dummy.next;
}
};# Definition for a Node.
# class Node:
# def __init__(self, x: int, next: 'Node' = None, random: 'Node' = None):
# self.val = int(x)
# self.next = next
# self.random = random
class Solution:
def copyRandomList(self, head: 'Optional[Node]') -> 'Optional[Node]':
if not head:
return None
curr = head
while curr:
clone = Node(curr.val, curr.next, None)
curr.next = clone
curr = clone.next
curr = head
while curr:
clone = curr.next
clone.random = curr.random.next if curr.random else None
curr = clone.next
dummy = Node(0)
tail = dummy
curr = head
while curr:
clone = curr.next
curr.next = clone.next
tail.next = clone
tail = clone
curr = curr.next
return dummy.next/**
* // Definition for a Node.
* function Node(val, next, random) {
* this.val = val;
* this.next = next;
* this.random = random;
* }
*/
/**
* @param {Node} head
* @return {Node}
*/
var copyRandomList = function(head) {
if (!head) return null;
let curr = head;
while (curr) {
const clone = new Node(curr.val, curr.next, null);
curr.next = clone;
curr = clone.next;
}
curr = head;
while (curr) {
const clone = curr.next;
clone.random = curr.random ? curr.random.next : null;
curr = clone.next;
}
const dummy = new Node(0, null, null);
let tail = dummy;
curr = head;
while (curr) {
const clone = curr.next;
curr.next = clone.next;
tail.next = clone;
tail = clone;
curr = curr.next;
}
return dummy.next;
};
Comments