题目
分析
本题要求我们对一个特殊的链表进行深拷贝。如果是普通链表,我们可以直接按照遍历的顺序创建链表节点。而本题中因为随机指针的存在,当我们拷贝节点时,「当前节点的随机指针指向的节点」可能还没创建,因此我们需要变换思路。一个可行方案是,我们利用回溯的方式,让每个节点的拷贝操作相互独立。对于当前节点,我们首先要进行拷贝,然后我们进行「当前节点的后继节点」和「当前节点的随机指针指向的节点」拷贝,拷贝完成后将创建的新节点的指针返回,即可完成当前节点的两指针的赋值。
即本题难点在于该随机指针在深拷贝时其内容尚未创建。
解决思路:官方解题思路有两个,其中学习第二种。对于本题难点,可以在第一遍遍历该链表的时候就new一遍所有的节点,保证在后续深拷贝时每一个random指针的对象都已经被创建。
具体的:
1、遍历链表,在每一个节点之后new一个节点,将new的节点插入原链表中,这样保证了只要原链表存在一个节点,这个节点之后必定有我们新new的节点。
2、遍历新链表,对原节点进行遍历,遍历时读取random指针指向的节点,将该节点后的new节点的random指针指向random->next,一遍遍历之后,所有新new的节点的random都会指向新new的节点。
3、遍历新链表,断链。恢复原链表,将新new的节点连接起来构成res。
代码
/*
// 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;
for(Node* p = head; p ; p = p->next->next){
Node* newNode = new Node(p->val);
if(p->next){
newNode->next = p->next;
p->next = newNode;
}else{
newNode->next = nullptr;
p->next = newNode;
}
}
for(Node* p = head; p; p = p->next->next){
Node* randomP = p->random;
if(randomP){
p->next->random = randomP->next;
}else{
p->next->random = nullptr;
}
}
Node* res = head->next;
for(Node* p = head; p;p = p->next){
Node* q = p->next;
p->next = p->next->next;
if(p->next){
q->next = q->next->next;
}else{
q->next = nullptr;
}
}
return res;
}
};
解法2
使用hashmap,遍历当前链表,对于每个节点都new一个与它一样的节点,使用hashmap保存这两个节点之间的映射关系。
在将所有的节点相连的过程中,新链表节点的Next指针应该指向hashmap[cur->next],因为cur->next对应的节点也new了一个节点,在hashmap[cur->next]中保存,random同理。新链表节点的random指针应该指向hashmap[cur->random]。
代码
class Solution {
public:
Node* copyRandomList(Node* head) {
if(!head){
return NULL;
}
unordered_map<Node*,Node*> hashmap;
Node* p = head;
while(p){
hashmap.emplace(p,new Node(p->val));
p=p->next;
}
p = head;
while(p){
hashmap[p]->next = hashmap[p->next];
hashmap[p]->random = hashmap[p->random];
p=p->next;
}
return hashmap[head];
}
};