题目描述:给出一个链表,每个节点包含一个额外增加的随机指针可以指向链表中的任何节点或空的节点。返回一个深拷贝的链表。
首先,我先解释一下题目的意思。就是完全按照给出的链表的关系复制链表,比如,原链表中值为1的节点的next指向的是值为2的节点,随机指针指向值为3的节点,那么,复制之后,还是应该满足这样的指向关系,
所谓深拷贝,就是开辟了内存,新对象指向新的内存,而不是和原对象一样指向同一片内存,两个对象的各自改变,不会互相影响
我们当然第一想法就是一个个复制节点,但是难度在于什么呢?复制前面节点的时候并不知道他要指向的节点(不论是随机还是next)在哪个地址。所以,直接简单粗暴复制是肯定不行的。
1、哈希表1
思路:创建一个键值和值均为RandomListNode*型哈希表,需要O(n)的空间复杂度,o(n)的时间复杂度
1、首先复制数据值和值间的next关系
2、复制random关系(此题的难点)
此方法非常简洁,体会到了哈希表的强大。
/*
struct RandomListNode {
int label;
struct RandomListNode *next, *random;
RandomListNode(int x) :
label(x), next(NULL), random(NULL) {
}
};
*/
class Solution {
public:
RandomListNode* Clone(RandomListNode* pHead)
{
map<RandomListNode*,RandomListNode*>temp;
RandomListNode*s=new RandomListNode(0);
RandomListNode*head=s;
RandomListNode*cur=pHead;
//复制结点值和next关系
while(cur!=NULL)
{
RandomListNode*newnode=new RandomListNode(cur->label);
temp[cur]=newnode;//原链表结点对应的复制链表结点,关键点所在
s->next=newnode;
s=s->next;
cur=cur->next;
}
cur=pHead;//复制随机指针
while(cur!=NULL)
{
temp[cur]->random=temp[cur->random];//即newnode->random=temp[cur->random],关键点所在
cur=cur->next;
}
return head->next;
}
};
2、非哈希表 (剑指offer解法) o(1)的空间复杂度,o(n)的时间复杂度
假设开始数组是1->2->3->4 。
1、第一遍扫的时候巧妙运用next指针,扫描过程中 先建立copy节点 1->1`->2->2`->3->3`->4->4`,
2、 第二遍copy的时候去建立边的copy, 拆分节点, 一边扫描一边拆成两个链表,这里用到两个dummy node。第一个链表变回 1->2->3 , 然后第二变成 1`->2`->3` 。如下图所示
RandomListNode *copyRandomList(RandomListNode *head) {
// write your code here
if(head==NULL){
return head;
}
RandomListNode*p=head;
while(p!=NULL){
RandomListNode*newnode=new RandomListNode(p->label);
newnode->next=p->next;
p->next=newnode;
p=newnode->next;
}
//复制random
p=head;
while(p!=NULL&&p->next!=NULL){
p->next->random=p->random==NULL?NULL:p->random->next; //不懂看下图
p=p->next->next;
}
//将链表分成两个链表
p=head;
RandomListNode* newnode=head->next;
RandomListNode*q=newnode;
while(p!=NULL){
p->next=newnode->next;
if(newnode->next!=NULL)
newnode->next=newnode->next->next;
p=p->next;
newnode=newnode->next;
}
return q;
}
};