题目描述
输入一个复杂链表(每个节点中有节点值,以及两个指针,一个指向下一个节点,另一个特殊指针random指向一个随机节点),请对此链表进行深拷贝,并返回拷贝后的头结点。(注意,输出结果中请不要返回参数中的节点引用,否则判题程序会直接返回空)
题目图示
如图,结点的next
的指向下一结点,random
指针指向随机结点。
解题思路1:一般法
要实现深复制,应把原来的next
指针的和random
指针都实现复制。
- 对于
next
指针的复制,需要按原来链表的关系逐步复制(新建结点,然后对应好结点的下一个结点(next
)) - 对于
random
指针的复制,由于其指向是随机的,因此需要从原来的整个链表里进行查找
由于链表中的random
指针是随机的,需要遍历一遍列表才能完成复制,在寻找结点时需要从查找,因此这种方法的时间复杂度较高,为
O
(
n
2
)
O(n^2)
O(n2),空间复杂度为
O
(
n
)
O(n)
O(n)
代码实现
/*
struct RandomListNode {
int label;
struct RandomListNode *next, *random;
RandomListNode(int x) :
label(x), next(NULL), random(NULL) {
}
};
*/
class Solution {
public:
RandomListNode* Clone(RandomListNode* pHead){
if(!pHead)return NULL;
RandomListNode *reHead;
reHead=copy_next(pHead);
return copy_rand(pHead,reHead);
}
RandomListNode* copy_next(RandomListNode* pHead){
RandomListNode *head=NULL,*tmp,*ptr,*last;
tmp=pHead;
while(tmp){
ptr=new RandomListNode(tmp->label);
if(head) last->next=ptr;
else head=ptr;
last=ptr;
tmp=tmp->next;
}
return head;
}
RandomListNode *copy_rand(RandomListNode *pHead,RandomListNode *reHead){
RandomListNode *tmp=pHead,*ratmp,*retmp=reHead;
int index,i;
while(tmp){
if(tmp->random){
index=look(pHead,tmp->random);
ratmp=reHead;
for(i=0;i<index;i++)ratmp=ratmp->next;
retmp->random=ratmp;
}
retmp=retmp->next;
tmp=tmp->next;
}
return reHead;
}
int look(RandomListNode *pHead,RandomListNode *tar){
RandomListNode *tmp=pHead;
int i;
for(i=0;tmp;i++){
if(tmp==tar)return i;
tmp=tmp->next;
}
}
};
运行时间:2ms
占用内存:504k
解题思路2:哈希法
我们可以先将原链表的next
完整地进行复制,同时将原链表和复制链表的对应关系存在哈希表里。
然后再复制random
指针,只需要在哈希表里查找到对应的结点并连接就完成了复制。
这是一种以空间换取空间的方法,时间复杂度为 O ( n ) O(n) O(n),空间复杂度为 O ( n ) O(n) O(n)。
代码实现
/*
struct RandomListNode {
int label;
struct RandomListNode *next, *random;
RandomListNode(int x) :
label(x), next(NULL), random(NULL) {
}
};
*/
class Solution {
public:
RandomListNode* Clone(RandomListNode* pHead){
if(!pHead)return NULL;
return hash_copy(pHead);
}
RandomListNode* hash_copy(RandomListNode* pHead){
RandomListNode *head=NULL,*tmp,*ptr,*last,*pc;
tmp=pHead;
map <RandomListNode *,RandomListNode *> tab;
while(tmp){
ptr=new RandomListNode(tmp->label);
tab[tmp]=ptr;
if(head) last->next=ptr;
else head=ptr;
last=ptr;
tmp=tmp->next;
}
tmp=pHead,pc=head;
while(tmp){
if(tmp->random)pc->random=tab[tmp->random];
tmp=tmp->next;
pc=pc->next;
}
return head;
}
};
运行时间:2ms
占用内存:388k
解题思路3:推荐解法
可以在原链表的结点后面插入一个值相同的结点,这样我们在复制random
所指结点的时候只需要让该结点的下一结点指向random
指向结点的下一结点即可。
以上操作完成后的链表结构如下图所示,我们要提取结果的话只需要将偶数位置上的结点提取出来即可。
NOTE:不能改变原链表结构
由于我们已经改变了原链表的结构,因此需要恢复原链表。
代码实现
/*
struct RandomListNode {
int label;
struct RandomListNode *next, *random;
RandomListNode(int x) :
label(x), next(NULL), random(NULL) {
}
};
*/
class Solution {
public:
RandomListNode* Clone(RandomListNode* pHead){
if(!pHead)return NULL;
RandomListNode *res;
res=copy_next(pHead);
res=copy_random(res);
res=result(res);
return res;
}
RandomListNode *copy_next(RandomListNode* pHead){
RandomListNode *tmp=pHead,*pn,*ptr;
while(tmp){
pn=tmp->next;
ptr=new RandomListNode(tmp->label);
tmp->next=ptr;
ptr->next=pn;
tmp=pn;
}
return pHead;
}
RandomListNode *copy_random(RandomListNode* pHead){
RandomListNode *tmp,*pn;
tmp=pHead;
while(tmp){
if(tmp->random)tmp->next->random=tmp->random->next;
tmp=tmp->next->next;
}
return pHead;
}
RandomListNode *result(RandomListNode* pHead){
RandomListNode *tmp,*node,*last,*reHead;
tmp=pHead,node=pHead;
last=tmp->next,reHead=last,tmp=last->next;
node->next=tmp;
while(tmp){
node=tmp;
last->next=tmp->next;
last=tmp->next;
tmp=last->next;
node->next=tmp;
}
return reHead;
}
};
运行时间:2ms
占用内存:504k