随机链表的复制
题目要求
你需要实现一个算法来复制一个特殊的链表,在这个链表中,每个节点除了有一个next
指针指向下一个节点,还有一个random
指针,它可以指向链表中的任意节点或者是空节点。你的任务是创建这个链表的一个深拷贝,这意味着你需要创建一份完全新的节点集合,使得:
- 每个新节点的值与原链表中的对应节点的值相同。
- 新节点的
next
指针指向复制链表中的下一个新节点。 - 新节点的
random
指针指向复制链表中的新节点,这些新节点对应于原链表中被random
指针指向的节点。 - 不应有任何指针指向原链表中的节点,即复制链表应该是完全独立的。
最终,你需要返回新复制链表的头节点。
解题思路
解决这个问题的关键是如何处理random
指针的复制。以下是一个高效的解题步骤:
-
遍历原链表,创建新节点:
- 遍历原链表,对于每个原节点创建一个新节点。
- 将新节点插入到原节点和原节点的下一个节点之间。
- 例如,原链表是 A -> B -> C,变换后的链表是 A -> A' -> B -> B' -> C -> C',其中 A', B', C'是新创建的节点的深拷贝。
-
复制
random
指针:- 再次遍历链表,复制
random
指针。由于新节点是紧跟在原节点后面的,可以通过original_node.random.next
来设置新节点的random
指针(如果original_node.random
不为空的话)。
- 再次遍历链表,复制
-
拆分链表:
- 最后,将这个交错的链表拆分为原链表和复制链表。
- 遍历链表,将原节点的
next
指向next.next
,将复制节点的next
指向next.next
(如果next
不为空的话)。 - 同时恢复原链表的结构。
这个方法的优点是不需要额外的空间来存储节点间的对应关系,因为复制节点紧跟在原节点后面,可以通过原链表直接定位到复制链表的节点。这样,我们就可以在不使用额外空间的情况下完成random
指针的复制。
Golang 版本
type Node struct {
Val int
Next *Node
Random *Node
}
func copyRandomList(head *Node) *Node {
if head == nil {
return nil
}
// Step 1: Create a new node for each node and insert it right after the original one.
for current := head; current != nil; current = current.Next.Next {
newNode := &Node{Val: current.Val}
newNode.Next = current.Next
current.Next = newNode
}
// Step 2: Copy the random pointers for the new nodes.
for current := head; current != nil; current = current.Next.Next {
if current.Random != nil {
current.Next.Random = current.Random.Next
}
}
// Step 3: Separate the combined list into the original list and the copied list.
pseudoHead := &Node{}
copyCurrent := pseudoHead
for current := head; current != nil; current = current.Next {
// Extract the copy
copyCurrent.Next = current.Next
copyCurrent = copyCurrent.Next
// Restore the original list
current.Next = current.Next.Next
}
return pseudoHead.Next
}
这段代码实现了上述的解题思路,首先在原链表的每个节点后面插入了一个新的节点,然后复制了random
指针,最后将这个交错的链表拆分成了原链表和复制链表。这样,我们就得到了原链表的深拷贝。
C++版本
#include <iostream>
using namespace std;
// 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;
// Step 1: Create a new node for each node and insert it right after the original one.
for (Node* node = head; node != nullptr; node = node->next->next) {
Node* newNode = new Node(node->val);
newNode->next = node->next;
node->next = newNode;
}
// Step 2: Copy the random pointers for the new nodes.
for (Node* node = head; node != nullptr; node = node->next->next) {
if (node->random) {
node->next->random = node->random->next;
}
}
// Step 3: Separate the combined list into the original list and the copied list.
Node* newHead = head->next;
for (Node* node = head; node != nullptr; node = node->next) {
Node* newNode = node->next;
node->next = newNode->next;
if (newNode->next) newNode->next = newNode->next->next;
}
return newHead;
}
};
int main() {
// Use the Solution class as you want to test the copyRandomList function.
return 0;
}
这段 C++代码实现了链表的深拷贝,其中包含了对random
指针的特殊处理。它首先在每个原始节点后面插入一个新的节点,然后设置新节点的random
指针,最后将这个交错的链表拆分成原始链表和复制链表。
Python 版本
class Node:
def __init__(self, val=0, next=None, random=None):
self.val = val
self.next = next
self.random = random
def copyRandomList(head):
if not head:
return None
# Step 1: Create a new node for each node and insert it right after the original one.
current = head
while current:
new_node = Node(current.val, current.next)
current.next = new_node
current = new_node.next
# Step 2: Copy the random pointers for the new nodes.
current = head
while current:
if current.random:
current.next.random = current.random.next
current = current.next.next
# Step 3: Separate the combined list into the original list and the copied list.
pseudo_head = Node(0)
copy_current, current = pseudo_head, head
while current:
next_orig = current.next.next
# Extract the copy
copy_current.next = current.next
copy_current = copy_current.next
# Restore the original list
current.next = next_orig
current = next_orig
return pseudo_head.next
这段 Python 代码实现了链表的深拷贝,包括对random
指针的复制。它首先在每个原始节点后面插入一个新的节点,然后设置新节点的random
指针,最后将这个交错的链表拆分成原始链表和复制链表。
Java 版本
class Node {
int val;
Node next;
Node random;
public Node(int val) {
this.val = val;
this.next = null;
this.random = null;
}
}
public class Solution {
public Node copyRandomList(Node head) {
if (head == null) {
return null;
}
// Step 1: Create a new node for each node and insert it right after the original one.
Node current = head;
while (current != null) {
Node newNode = new Node(current.val);
newNode.next = current.next;
current.next = newNode;
current = newNode.next;
}
// Step 2: Copy the random pointers for the new nodes.
current = head;
while (current != null) {
if (current.random != null) {
current.next.random = current.random.next;
}
current = current.next.next;
}
// Step 3: Separate the combined list into the original list and the copied list.
Node pseudoHead = new Node(0);
Node copy, copyCurrent = pseudoHead;
current = head;
while (current != null) {
next = current.next.next;
// Extract the copy
copy = current.next;
copyCurrent.next = copy;
copyCurrent = copy;
// Restore the original list
current.next = next;
current = next;
}
return pseudoHead.next;
}
}
这段 Java 代码实现了链表的深拷贝,包括对random
指针的复制。它首先在每个原始节点后面插入一个新的节点,然后设置新节点的random
指针,最后将这个交错的链表拆分成原始链表和复制链表。
Rust 版本
#![allow(unused)] fn main() { // Definition for a Node. #[derive(Clone, Debug)] pub struct Node { pub val: i32, pub next: Option<Box<Node>>, pub random: Option<Box<Node>>, } impl Node { fn new(val: i32) -> Node { Node { val, next: None, random: None, } } } pub fn copy_random_list(head: Option<Box<Node>>) -> Option<Box<Node>> { let mut head = head; let mut current = head.as_mut(); // Step 1: Create a new node for each node and insert it right after the original one. while let Some(node) = current { let new_node = Box::new(Node::new(node.val)); let next = node.next.take(); node.next = Some(new_node); node.next.as_mut().unwrap().next = next; current = node.next.as_mut().unwrap().next.as_mut(); } // Step 2: Copy the random pointers for the new nodes. current = head.as_mut(); while let Some(node) = current { node.next.as_mut().unwrap().random = node.random.as_ref().map(|random_node| { Box::new(Node::new(random_node.val)) }); current = node.next.as_mut().unwrap().next.as_mut(); } // Step 3: Separate the combined list into the original list and the copied list. let mut pseudo_head = Some(Box::new(Node::new(0))); let mut tail = pseudo_head.as_mut(); current = head.as_mut(); while let Some(node) = current { let next = node.next.as_mut().unwrap().next.take(); tail.as_mut().unwrap().next = node.next.take(); tail = tail.unwrap().next.as_mut(); node.next = next; current = node.next.as_mut(); } pseudo_head.unwrap().next } }
这段 Rust 代码实现了链表的深拷贝,包括对random
指针的复制。它首先在每个原始节点后面插入一个新的节点,然后设置新节点的random
指针,最后将这个交错的链表拆分成原始链表和复制链表。由于 Rust 的所有权和借用规则,这段代码在处理指针和节点的所有权时需要特别注意。
总结
上面给出了针对复制带随机指针的链表的解法,涵盖了 Golang、C++、Python、Java 和 Rust 的代码实现。
这个问题的解决思路是通过三个步骤来完成链表的深拷贝:首先在每个原始节点后面插入一个新的节点,然后设置新节点的random
指针,最后将这个交错的链表拆分成原始链表和复制链表。这样就能够实现对带随机指针的链表进行深度复制,保持了原链表和复制链表的独立性。