力扣 Hot 100 刷题记录 - 随机链表的复制
力扣 Hot 100 刷题记录 - 随机链表的复制
题目描述
随机链表的复制 是力扣 Hot 100 中的一道经典题目,题目要求如下:
给你一个长度为 n
的链表,每个节点包含一个额外增加的随机指针 random
,该指针可以指向链表中的任何节点或空节点。
构造这个链表的 深拷贝。深拷贝应该正好由 n
个全新节点组成,其中每个新节点的值都设为其对应的原节点的值。新节点的 next
指针和 random
指针也都应指向复制链表中的新节点,并使原链表和复制链表中的这些指针能够表示相同的链表状态。复制链表中的指针都不应指向原链表中的节点。
示例 1:
输入:head = [[7,null],[13,0],[11,4],[10,2],[1,0]]
输出:[[7,null],[13,0],[11,4],[10,2],[1,0]]
示例 2:
输入:head = [[1,1],[2,1]]
输出:[[1,1],[2,1]]
示例 3:
输入:head = [[3,null],[3,0],[3,null]]
输出:[[3,null],[3,0],[3,null]]
解题思路
这道题的核心是复制一个带有随机指针的链表,难点在于如何处理 random
指针的指向。常用的方法是 哈希表 或 原地复制,具体步骤如下:
-
哈希表法:
- 使用哈希表存储原节点和新节点的映射关系。
- 第一次遍历链表,创建新节点并建立映射。
- 第二次遍历链表,根据哈希表设置新节点的
next
和random
指针。
-
原地复制法:
- 在原链表的每个节点后面插入一个新节点,新节点的值与原节点相同。
- 设置新节点的
random
指针。 - 分离原链表和新链表。
C++ 代码实现
方法一:哈希表法
/*
// Definition for a Node.
class Node {
public:
int val;
Node* next;
Node* random;
Node(int _val) {
val = _val;
next = nullptr;
random = nullptr;
}
};
*/
class Solution {
public:
Node* copyRandomList(Node* head) {
if (!head) return nullptr;
// 哈希表:原节点 -> 新节点
unordered_map<Node*, Node*> map;
// 第一次遍历:创建新节点并建立映射
Node* curr = head;
while (curr) {
map[curr] = new Node(curr->val);
curr = curr->next;
}
// 第二次遍历:设置新节点的 next 和 random 指针
curr = head;
while (curr) {
map[curr]->next = map[curr->next];
map[curr]->random = map[curr->random];
curr = curr->next;
}
return map[head]; // 返回新链表的头节点
}
};
### 方法一:原地复制法
class Solution {
public:
Node* copyRandomList(Node* head) {
if (!head) return nullptr;
// 第一步:在每个原节点后面插入一个新节点
Node* curr = head;
while (curr) {
Node* newNode = new Node(curr->val);
newNode->next = curr->next;
curr->next = newNode;
curr = newNode->next;
}
// 第二步:设置新节点的 random 指针
curr = head;
while (curr) {
if (curr->random) {
curr->next->random = curr->random->next;
}
curr = curr->next->next;
}
// 第三步:分离原链表和新链表
Node* newHead = head->next;
curr = head;
while (curr) {
Node* newNode = curr->next;
curr->next = newNode->next;
if (newNode->next) {
newNode->next = newNode->next->next;
}
curr = curr->next;
}
return newHead; // 返回新链表的头节点
}
};