题目链接:https://leetcode.cn/problems/copy-list-with-random-pointer/description/
📕题目要求:
给你一个长度为 n 的链表,每个节点包含一个额外增加的随机指针 random ,该指针可以指向链表中的任何节点或空节点。
构造这个链表的 深拷贝。 深拷贝应该正好由 n 个 全新 节点组成,其中每个新节点的值都设为其对应的原节点的值。新节点的 next 指针和 random 指针也都应指向复制链表中的新节点,并使原链表和复制链表中的这些指针能够表示相同的链表状态。复制链表中的指针都不应指向原链表中的节点 。
例如,如果原链表中有 X 和 Y 两个节点,其中 X.random --> Y 。那么在复制链表中对应的两个节点 x 和 y ,同样有 x.random --> y 。
返回复制链表的头节点。
用一个由 n 个节点组成的链表来表示输入/输出中的链表。每个节点用一个 [val, random_index] 表示:
- val:一个表示 Node.val 的整数。
- random_index:随机指针指向的节点索引(范围从 0 到 n-1);如果不指向任何节点,则为 null 。
你的代码 只 接受原链表的头节点 head 作为传入参数。
🧠解题思路
方案一:单独将复制的结点组合为复制链表
该方案是效率比较低的一种,也是大家能够最容易想到的一种方法。第一次遍历原链表,同时每次迭代时申请一个新结点,复制原链表结点的信息(val)给新结点,再将结点进行连接迭代,组成链表。第二次迭代,也是该题最关键的一步,确定random的指向,通过原链表random指向的相对位置进行类比推理,最终确定指向。
时间复杂度为:O(N^2)
方案二:单链表的插入操作大幅减少时间复杂度
将原结点与拷贝结点建立出某种关系(拷贝结点插在了在原结点后),通过插入操作将拷贝结点插入到原结点之后。然后遍历控制random的指向。
时间复杂度为:O(N)
🍭代码示例
玩法一代码示例如下:
struct Node* copyRandomList(struct Node* head)
{
if(head==NULL)
{
return head;
}
struct Node* newhead = NULL;
struct Node* cur = NULL;
struct Node* next = head;
if(newhead==NULL)
{
struct Node* newnode = (struct Node*)malloc(sizeof(struct Node));
newnode->next = NULL;
newnode->random = NULL;
cur = newhead = newnode;
cur->val = next->val;
next = next->next;
}
while(next)
{
struct Node* newnode = (struct Node*)malloc(sizeof(struct Node));
newnode->next = NULL;
newnode->random = NULL;
cur->next = newnode;
cur = cur->next;
cur->val = next->val;
next = next->next;
}
cur = newhead;
next = head;
while(next)
{
int k = 0;
struct Node* mov1 = head;
struct Node* mov2 = newhead;
if(next->random==NULL)
{
cur->random=NULL;
}
else
{
while(mov1!=next->random)
{
mov1 = mov1->next;
k++;
}
while(k--)
{
mov2 = mov2->next;
}
cur->random = mov2;
}
next = next->next;
cur = cur->next;
}
return newhead;
}
玩法二代码示例如下:
struct Node* copyRandomList(struct Node* head)
{
struct Node* cur = head;
//1.对拷贝链表与原链表建立关系:拷贝节点插入到原结点的后面
while(cur)
{
struct Node* copy = (struct Node*)malloc(sizeof(struct Node));
struct Node* next = cur->next;
copy->val = cur->val;
//插入
cur->next = copy;
copy->next = next;
//迭代
cur = next;
}
//2.控制拷贝结点的random
cur = head;
while(cur)
{
struct Node* copy = cur->next;
if(cur->random==NULL)
{
copy->random = NULL;
}
else
{
copy->random = cur->random->next;
}
//迭代
cur = copy->next;
}
//将拷贝结点解下尾插组成拷贝链表,并恢复原链表。
struct Node* copyHead = NULL;
struct Node* copyTail = NULL;
cur = head;
while(cur)
{
struct Node* copy = cur->next;
struct Node* next = copy->next;
if(copyTail == NULL)
{
copyHead = copyTail = copy;
}
else
{
copyTail->next = copy;
copyTail = copyTail->next;
}
cur->next = next;
cur = next;
}
return copyHead;
}
这就是我对本题的理解,如果大家有更优的解,欢迎交流,一起进步!