LeetCode题解
前言
在刷题的过程中,遇到了一个很棒的思路,与是便记录了下来!!!
😊😊😊
复制带随机指针的链表
题目描述:
➡️挑战链接⬅️
分析:
题目描述的很长,但是仔细阅读的话,我们会发现题目想表达的意思很明确;
就是将题目给的链表,完整的复制一份,包括复制各节点之间的链接关系;
首先链接关系中,next指针域的节点指针很好复制,现在问题是怎么复制random指针域的关系?
第一种思路:
我们可以相对位置的关系,来找到拷贝链表的random;
什么个意思嘞?
就比如,声明: 下面的链表是对原链表的拷贝(此时还没有处理random指针域),copy表示是对饮原链表节点cur的拷贝。
既然copy是cur的拷贝,那么cur在原链表的相对位置,与copy节点在拷贝链表的相对位置应该是一样的,对吧!就比如图中cur相对于原链表来说在距离原链表偏移量为2的位置,同理对应的copy节点也应该在拷贝链表偏移量为2的地方;
好,有了以上的认识,我们处理起拷贝节点的指针就比较容易了;
按照刚才的道理,那么我cur->random是不是也有个相对位置,那我copy->random的相对位置是不是应该和cur->random的相对位置一样?
该是这样吧,就比如图中cur->random在相对于链表位置为4的位置,那么我的copy->random也应该在相对与链表位置为4的地方,也就是下图所表示的地方:
你看这样我们是不是就找到copy节点的random指针了;是不是就完成了对于random指针的一份拷贝;
上面的过程总结一下就是:
1、先形成一份拷贝链表(还没有复制随机指针的关系)
2、根据copy节点,求出原节点(cur节点)的随机指针相对链表的相对位置,然后我们根据该相对位置,去拷贝链表里面寻找随机指针;最后我们一定会找到copy节点应该链接的随即指针,链接起来就好了;
时间复杂度:O(N^2)
空间复杂度:O(1)
代码实现:
/**
* Definition for a Node.
* struct Node {
* int val;
* struct Node *next;
* struct Node *random;
* };
*///
//搜索原节点的random指针相对于原链表的相对位置
int SearchStep(struct Node*head,struct Node*random)
{
if(random==NULL)//如果原节点的随机指针是NULL,直接返回-1,就好了
return -1;
struct Node*cur=head;
int step=0;
while(cur)
{
if(cur==random)
break;
else
{
step++;
cur=cur->next;
}
}
return step;
}
//根据相对位置去拷贝链表里面寻找copy节点的随机指针
struct Node*CopyRandom(struct Node*head,int step)
{
struct Node*cur=head;
while(step--)
cur=cur->next;
return cur;
}
struct Node* copyRandomList(struct Node* head) {
struct Node*CopyHead=NULL;//存储拷贝链表的头
struct Node*cur=head;
struct Node*copy=NULL;
struct Node*tail=NULL;
while(cur)//形成拷贝链表
{
copy=(struct Node*)malloc(sizeof(struct Node));
copy->val=cur->val;
if(CopyHead==NULL)
{
CopyHead=tail=copy;
tail->next=NULL;
}
else
{
tail->next=copy;
tail=copy;
tail->next=NULL;
}
cur=cur->next;
}
cur=head;
copy=CopyHead;
while(cur)
{
struct Node*random=cur->random;
int step=SearchStep(head,random);//定位cur节点的随机指针在链表中的相对位置
if(step==-1)
{
copy->random=NULL;
}
else
{
//根据相对位置去定位copy节点的随机指针
struct Node*copyrandom=CopyRandom(CopyHead,step);
copy->random=copyrandom;
}
cur=cur->next;
copy=copy->next;
}
return CopyHead;
}
第二种方法:
这个方法,真的棒的出奇!!我很佩服那些能想出这种逆天的方法的,该方法直接将时间复杂度缩小到O(N);
思路:
我们现在夫吧拷贝节点链接起来,我们现在将它们放在对应原节点的后面,并把它们链接起来,说白了就是将拷贝节点链接在原节点的屁股后面!!😊😊😊画图就是这样:
链接完毕过后,我们就该处理拷贝节点的随机指针问题了;
以上面为例
我们copy是cur的一份拷贝嘛,那么现在我cur的random是null,那我copy是不是也应该模仿者cur让我的random也指向跟cur的random的一份一摸一样的节点(不能是同一个节点)cur的random指向NULL,那我copy->random也应该指向空;好了现在我更新我的cur和copy:
我copy是cur的一份拷贝,我cur所做的一切动作,我copy都会跟着一起模仿,现在我cur的random指向7,那么好了我copy->random是不是也应该指向一份7(copy不能和cur指向同一个7),那么我是不是就应该指向cur节点的random的一份拷贝啊!那这份拷贝在哪里呢?
是不是就在cur节点的random节点的后面啊,我们刚开始就将原链表的每个节点的拷贝节点链接在了后面,于是我们 copy->random=cur->random->next; 这样就解释了我们刚才第一步为什么要把拷贝节点全部连接在原节点的后面;这样一来我们不就解决了copy节点的random指针的复制问题了嘛;
如果上面的描述还没让你明白,我们在举个通俗一点的例子:
假设cur和copy是双胞胎,cur是大哥,copy是小弟;
random就相当于女朋友;
现在大哥的女朋友很能干,很贤惠啊!小弟呢也想找个这样的女朋友,于是他就叫他大哥给他介绍个,大哥呢,就想到了女朋友的妹妹是跟女朋友一摸一样的人,于是嘞,他就将女朋友的妹妹介绍给了弟弟;而这个女朋友的妹妹在哪里嘞?是不是通过女朋友就能找到?(就在女朋友的后面);
于是弟弟就通过哥哥女朋友的介绍链接到了妹妹的存在;
但是现在我拷贝节点的指针指向问题虽然是解决了,但是我的原节点和拷贝节点链接在了一起,我们得想办法恢复原链表的链接关系和剪下拷贝节点嘛,这样我们才能满足题意;
综上所述:
1、将拷贝节点链接在对应原节点的后面;
2、解决掉拷贝节点的随机指针指向问题;
3、恢复原链表和剪下拷贝节点并形成拷贝链表;
思路虽然有了,但是代码写起来还是得画图才行,我么先来解决第一条:
第二步复制随机链表:
第三步恢复原链表和剪下拷贝节点并形成拷贝链表
代码实现:
struct Node* copyRandomList(struct Node* head) {
struct Node*cur=head;
struct Node*next=NULL;
struct Node*NewNode=NULL;
//1、开始链接链表
while(cur)
{
next=cur->next;
NewNode=(struct Node*)malloc(sizeof(struct Node));
NewNode->val=cur->val;
cur->next=NewNode;
NewNode->next=next;
cur=next;
}
//2、开始复制随机指针
cur=head;
struct Node*copy=NULL;
while(cur)
{
copy=cur->next;
if(cur->random==NULL)
{
copy->random=NULL;
}
else
{
copy->random=cur->random->next;
}
cur=copy->next;
}
//3、开始剪切链表和复原链表
cur=head;
struct Node*plist=NULL;//存储拷贝链表头节点
struct Node*tail=NULL;
while(cur)
{
copy=cur->next;
next=copy->next;
//开始剪切拷贝链表
if(tail==NULL)
plist=tail=copy;
else
{
tail->next=copy;
tail=copy;
tail->next=NULL;
}
cur->next=next;//复原原链表
cur=next;
}
return plist;
}
时间复杂度:O(N)
空间复杂度:O(1)