来自力扣的一道题目:138. 复制带随机指针的链表 - 力扣(LeetCode)
问题描述:
给你一个长度为 n 的链表,每个节点包含一个额外增加的随机指针 random ,该指针可以指向链表
中的任何节点或空节点。
构造这个链表的 深拷贝。 深拷贝应该正好由 n 个 全新 节点组成,其中每个新节点的值都设为其
对应的原节点的值。新节点的 next 指针和 random 指针也都应指向复制链表中的新节点,并使原
链表和复制链表中的这些指针能够表示相同的链表状态。复制链表中的指针都不应指向原链表中的
节点 。
示例1:
输入:head = [[7,null],[13,0],[11,4],[10,2],[1,0]]
输出:[[7,null],[13,0],[11,4],[10,2],[1,0]]
示例二:
输入:head = [[3,null],[3,0],[3,null]]
输出:[[3,null],[3,0],[3,null]]
对应的链表结构:
struct Node {
int val;
struct Node *next;
struct Node *random;
};
问题分析:
这道题的主要问题是将带随机链表的指针拷贝一份,并返回拷贝链表的头节点,这里有些人就会心
生想法,直接把原来的那个链表的头节点返回不就可以了吗?但很显然是过不了的,因为我们要返
回的头节点地址与原链表的头节点地址应该是不同的,如果是相同的,那还能叫拷贝吗?
那么我们应该如何拷贝呢:
如果我们创建一个新链表来进行拷贝的话,那么对应的随机指针和next指针有应该如何来存储,如
果直接进行赋值的话,因为对应的指针是不变的,所以相当于再次应用了原链表的指针,显然还是
行不通的。
正确办法有点不好想,在这我也只能直接讲,大家最好能有自己的理解和思考,具体分为三步:
1.在原节点的后面插入拷贝节点
这一步我们先不考虑random指针的关系,先进行链表的插入,将每个拷贝节点放在每个原节点的
后面,而拷贝的节点我们需要自己malloc,在拷贝之前要先把原链表的下一个节点存储下来,以此
来作为迭代进行插入拷贝节点。
2.拷贝节点的random指针,原节点的random->next。
如上图,对拷贝节点的random进行连接时,必须要将拷贝节点链接到拷贝节点上,切忌链接到原
节点上,会成为环形链表,同过上面的图示,我们应该不难理解了,连接应该是:
copy->random = cur->random->next(对应的随机节点的拷贝节点)。
当然,对于cur->random==NULL的,应该将copy->random=NULL。这是一种特殊情况。
3.将拷贝节点剪下来,链接成新链表,并回复原链表。
如上图,是这一步进行的步骤,首先将拷贝节点截取下来并尾插,其次让原链表还原,即
cur->next = cur->next->next;
尾插是要初始化两个节点,用来记录头和尾。
代码实现:
1.在原节点的后面插入拷贝节点
//插入拷贝节点
struct Node* cur = NULL;
cur= head;
while(cur)
{
struct Node* copy = (struct Node*)malloc(sizeof(struct Node));
copy->val = cur->val;
struct Node* next = cur->next;
//拷贝
cur->next = copy;
copy->next = next;
cur = next;
}
2.拷贝节点的random指针,原节点的random->next。
//拷贝节点的random
cur = head;
while (cur)
{
struct Node* copy = cur->next;
if (cur->random == NULL)
{
copy->random = NULL;
}
else
{
copy->random = cur->random->next;
}
cur = cur->next->next;
}
3.将拷贝节点剪下来,链接成新链表,并回复原链表。
//尾插拷贝链表
struct Node* copyhead =NULL,*copytail = NULL;
cur = head;
while(cur)
{
struct Node* copy = cur->next;
struct Node* next = copy->next;
//尾插
if(copyhead == NULL)
{
copyhead = copytail = copy;
}
else
{
copytail->next = copy;
copytail = copytail->next;
}
//恢复原链表
cur->next = next;
cur = next;
}
整体代码:
struct Node* copyRandomList(struct Node* head)
{
//插入拷贝节点
struct Node* cur = NULL;
cur = head;
while (cur)
{
struct Node* copy = (struct Node*)malloc(sizeof(struct Node));
copy->val = cur->val;
struct Node* next = cur->next;
//拷贝
cur->next = copy;
copy->next = next;
cur = next;
}
//拷贝节点的random
cur = head;
while (cur)
{
struct Node* copy = cur->next;
if (cur->random == NULL)
{
copy->random = NULL;
}
else
{
copy->random = cur->random->next;
}
cur = cur->next->next;
}
//尾插拷贝链表
struct Node* copyhead = NULL, * copytail = NULL;
cur = head;
while (cur)
{
struct Node* copy = cur->next;
struct Node* next = copy->next;
//尾插
if (copyhead == NULL)
{
copyhead = copytail = copy;
}
else
{
copytail->next = copy;
copytail = copytail->next;
}
//恢复原链表
cur->next = next;
cur = next;
}
return copyhead;
}
这道题是链表里面比较难搞的题,当大家真正理解了这道题之后,说明我们的链表就可以了!