剑指 Offer 35. 复杂链表的复制
题目描述
请实现 copyRandomList 函数,复制一个复杂链表。在复杂链表中,每个节点除了有一个 next 指针指向下一个节点,还有一个 random 指针指向链表中的任意节点或者 null。
示例 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]]
示例 4:
输入:head = []
输出:[]
解释:给定的链表为空(空指针),因此返回 null。
提示
-
-10000 <= Node.val <= 10000
-
Node.random 为空(null)或指向链表中的节点。
-
节点数目不超过 1000 。
作者:Krahets 链接:力扣 来源:力扣(LeetCode) 著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
解题思路
利用哈希表解法:
第一步:
建立哈希表
设置对应的key值和value值,key值为原来的链表,用原来链表中的每一个结构体作为key值,,并且用cur变量定位当前key值位置,
而用map[cur]来表示对应key值的value值。
第二步:
复制key中的val、next、random
复制的时候新建对应key值的结构体节点,并且将原链表中结构体的val值复制给map[cur](此处表示key值为cur的value对象)。
while (cur) {
// 复制当前节点并将其存入哈希表中
map[cur] = new Node(cur->val);
cur = cur->next; // 指针后移,处理下一个节点
}
创建好如图所示的几个结构体后,便开始复制原链表中的next值和random值,此时复制完next值即可将复制后的链表节点连接起来
cur = head; // 重新指向头节点
while (cur) {
map[cur]->next = map[cur->next]; // 将新节点的 next 指针指向下一个新节点
map[cur]->random = map[cur->random]; // 将新节点的随机指针指向原节点的随机指针所对应的新节点
cur = cur->next; // 移动到下一个原节点
}
注意其中的map[cur->next]与map[cur]->next的区别,前者[cur->next]是指一个key值,可以看到图中来理解为一个嵌套的哈希表,key值为cur的节点中,还有个key值为next的对应关系,所以map[cur->next]表示原链表节点中的next值。后者map[cur]->next,应该理解为map[cur]的next值,map[cur]在之前解释过表示为value的意思,即为key值节点所对应的复制后的节点的next值。
random的复制原理与next相同。
核心代码
Node* copyRandomList(Node* head) {
if (head == nullptr) return head; // 如果头节点为空,则直接返回空指针
Node* cur = head; // 创建指向当前节点的指针
unordered_map<Node*, Node*> map; // 创建哈希表,用于存储原节点与新节点的对应关系
while (cur) {
// 复制当前节点并将其存入哈希表中
map[cur] = new Node(cur->val);
cur = cur->next; // 指针后移,处理下一个节点
}
// 更新新链表中节点的 next 和 random 指针
cur = head; // 重新指向头节点
while (cur) {
map[cur]->next = map[cur->next]; // 将新节点的 next 指针指向下一个新节点
map[cur]->random = map[cur->random]; // 将新节点的随机指针指向原节点的随机指针所对应的新节点
cur = cur->next; // 移动到下一个原节点
}
return map[head]; // 返回头节点的对应新节点
}
完整代码
#include <iostream>
#include <unordered_map>
#include <string>
using namespace std;
// 定义链表节点结构体
struct Node {
int val; // 当前节点的值
Node* next; // 指向下一个节点的指针
Node* random; // 随机指针
Node(int x) : val(x), next(nullptr), random(nullptr) {} // 构造函数
};
// 复制带随机指针的链表
Node* copyRandomList(Node* head) {
if (head == nullptr) return head; // 如果头节点为空,则直接返回空指针
Node* cur = head; // 创建指向当前节点的指针
unordered_map<Node*, Node*> map; // 创建哈希表,用于存储原节点与新节点的对应关系
while (cur) {
// 复制当前节点并将其存入哈希表中
map[cur] = new Node(cur->val);
cur = cur->next; // 指针后移,处理下一个节点
}
// 更新新链表中节点的 next 和 random 指针
cur = head; // 重新指向头节点
while (cur) {
map[cur]->next = map[cur->next]; // 将新节点的 next 指针指向下一个新节点
map[cur]->random = map[cur->random]; // 将新节点的随机指针指向原节点的随机指针所对应的新节点
cur = cur->next; // 移动到下一个原节点
}
return map[head]; // 返回头节点的对应新节点
}
// 打印链表信息
void printList(Node* head) {
Node* cur1 = head; // 创建指向头节点的指针
while (cur1) { // 遍历链表,直到结束
cout << cur1->val << " "; // 输出当前节点的值
Node* cur2 = head; // 创建新指针,用于查找随机指针所对应的节点
int i = 0; // 计数器,用于记录随机指针所指向节点的位置
while (cur2) {
if (cur1->random == cur2) { // 如果当前随机指针与当前节点相同,则找到了随机指针所指向的节点
cout << i; // 输出该节点在链表中的位置,从 0 开始计数
break;
}
cur2 = cur2->next; // 向后移动指针
i++; // 更新计数器值
}
if (cur1->random == nullptr) cout << "null"; // 如果当前节点随机指针为空,则输出 null
cout << endl;
cur1 = cur1->next; // 移动到下一个节点
}
}
int main() {
// 创建原链表
int num,ran,cnt=1;
char c;
cout<<"输入对应val值:";
cin>>num;
Node* head=new Node(num);
Node* cur=head;
while(c!='\n'){
cin>>num;
cnt++;
while(cur->next!=nullptr){
cur=cur->next;
}
Node* newNode=new Node(num);
cur->next=newNode;
c=getchar();
}
Node* cur1=head;
cout<<"输入对应random值:";
// 输入链表节点的随机指针所指向的位置,并将其对应到相应的新链表节点上
for(int i=0;i<cnt;i++){
string s;
cin>>s; // 读入节点位置信息,使用字符串类型进行存储
if (s == "null") { // 如果输入的是 null,则随机指针为空
cur1->random = nullptr;
} else {
int ran = stoi(s); // 将字符串转换为整型,得到随机指针指向的节点位置
Node* cur2=head; // 创建指向头节点的新指针,用于查找随机指针所指向的节点
for(int j=0;j<ran;j++){ // 从头节点开始,往后移动指针,直到找到随机指针所指向的节点
cur2=cur2->next;
}
cur1->random=cur2; // 更新随机指针所指向的新节点
}
cur1=cur1->next; // 移动到下一个原节点对应的新节点
}
// 打印原链表信息
cout << "Original List:" << endl;
printList(head);
// 复制链表并打印信息
Node* newHead = copyRandomList(head);
cout << "Copied List:" << endl;
printList(newHead);
return 0;
}
-
时间复杂度 O(N) : 两轮遍历链表,使用 O(N) 时间。
-
空间复杂度 O(N) : 哈希表 dic 使用线性大小的额外空间。