138. 复制带随机指针的链表 - 力扣(LeetCode)
题目要求是把它给你的长度为N的链表复制一份返回,但是由于它有个random指向随机的的节点,如果创建新链表,每次遍历一下这个链表去复制到新链表的话时间复杂度很大,一但链表过长就不知道执行到猴年马月了,所以可以在原链表上用3个循环完成目的。
第一步,复制节点和节点中的值。我们可以把复制的节点(copy)链接在被复制节点(cur)的后面,然后把copy->next链接到cur的下一个节点(next),再把cur指向next,当cur为NULL时,完成第一个循环 。至于为什么不链接到cur的前面,下一步循环会提到。
如图所示
第二步,复制 随机指针random。将cur置为头节点,copy指向复制节点,即cur->next,将copy的random指针指向cur->random,由于复制节点copy->random指向的也是复制的节点,那么每个复制节点都在原节点random的后面(->next),可以赋值。这里要考虑两种情况:
第一种情况cur->random可能为NULL,则copy->random=NULL
第二中情况cur->random不为NULL,则copy->random=cur->random->next
所以要在循环中加一个if语句来判段原节点的random是否为NULL。复制下一个节点的random时cur直接指向copy->next即可,cur为NULL时结束循环。
在这个循环中就可以发现,如果复制的节点在原节点的前面,那么当你找到原节点时,random要链接到原节点的复制节点,是往前找了,单链表无法回头找节点,所以不能把复制的节点放在前面。
第三步,分裂,把复制的节点依次链接到新链头组成要返回的新的链表,即是尾插,再还原原链表。这时候就要创建一个新的头指针(copyHead)和尾指针(copyTail)来尾插链表,再返回copyHead即可。
完成循环后得到如图,循环结束的条件依旧是cur为NULL
实现代码如下
/**
* Definition for a Node.
* struct Node {
* int val;
* struct Node *next;
* struct Node *random;
* };
*/
struct Node* copyRandomList(struct Node* head) {
struct Node* cur=head;
while(cur)
{
struct Node* next=cur->next;
struct Node* copy=(struct Node*)malloc(sizeof(struct Node));//开辟新空间
copy->val=cur->val;//复制链表中的值
//链接
cur->next=copy;
copy->next=next;
cur=next; //当next(cur->next)=NULL时结束循环
}
cur=head;//回到头结点
while(cur)
{
struct Node* copy=cur->next;
if(cur->random==NULL) //判断cur的random是否指向NULL
{
copy->random=NULL;
}
else
{
copy->random=cur->random->next;//如果random指向的不为NULL 则把结点的复制结点给它
}
cur=copy->next; //后移,copy的下一个节点是原节点
}
cur=head; //再回到头节点
struct Node* copyHead=NULL,*copyTail=NULL; //新链表的头指针和尾指针,用来完成尾插
while(cur)
{
struct Node* copy=cur->next;
struct Node* next=copy->next;
cur->next=next;
if(copyTail==NULL)
{
copyHead=copyTail=copy;//初始化复制的链表
}
else //尾插
{
copyTail->next=copy;
copyTail=copyTail->next;
}
cur=next;
}
return copyHead;
}