目录
一、何为深拷贝?
二、题目
三、思路
1.拷贝节点插入到原节点后面
2.控制拷贝节点的random
3.脱离原链表 : 尾插的思想
四、代码
五、附加
一、何为深拷贝?
一个引用对象一般来说由两个部分组成:一个具名的Handle,也就是我们所说的声明(如变量)和一个内部(不具名)的对象,也就是具名Handle的内部对象。它在Manged Heap(托管堆)中分配,一般由新增引用对象的New方法是进行创建。深拷贝是指源对象与拷贝对象互相独立,其中任何一个对象的改动都不会对另外一个对象造成影响。比较典型的就是Value(值)对象,如预定义类型Int32,Double,以及结构(struct),枚举(Enum)等。
简而言之:
深拷贝就是拷贝一个一摸一样的链表出来。
二、题目
给你一个长度为 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
作为传入参数。
示例 1:
输入:head = [[7,null],[13,0],[11,4],[10,2],[1,0]] 输出:[[7,null],[13,0],[11,4],[10,2],[1,0]]
示例 2:
输入:head = [[1,1],[2,1]] 输出:[[1,1],[2,1]]
示例 3:
输入:head = [[3,null],[3,0],[3,null]] 输出:[[3,null],[3,0],[3,null]]
提示:
0 <= n <= 1000
-104 <= Node.val <= 104
Node.random
为null
或指向链表中的节点。
三、思路
1.拷贝节点插入到原节点后面
while (cur)
{
struct Node* copy = (struct Node*)malloc(sizeof(struct Node)); //每次都要开辟空间 //强制类型转换!
copy->val = cur->val; //赋值
//链接
struct Node* next = cur->next; //定义一个next指针,可不考虑顺序问题
cur->next = copy;
copy->next = next;
cur = next; //cur 指针在原链表往后走!
}
2.控制拷贝节点的random
关键:不为空,random->next = cur->random->next;为空,random->next = NULL;
while (cur)
{
struct Node* copy = cur->next; //不需要额外开辟空间,copy节点已经连接了原节点
if (cur->random == NULL)
copy->random = NULL;
else //cur->random要注意是不是空!
copy->random = cur->random->next; //copy的random指向的是拷贝节点
//迭代 //copy不需要迭代,copy是封装在循环内的指针,由cur控制!
cur = copy->next; //cur 在原链表迭代!
}
3.脱离原链表 : 尾插的思想
介绍尾插:用尾插的思想实现移除链表中的元素-CSDN博客
尾插不是插入到原链表的尾部,而是里用尾插的思想!tail是尾节点!而不是尾节点的下一个!
while (cur)
{
struct Node* copy = cur->next;
//脱离
if(CopyHead == NULL)
{
CopyHead = tail = copy; //tail是尾节点!而不是尾节点的下一个!
}
else
{
tail->next = copy;
tail = tail->next; //tail是尾节点!而不是尾节点的下一个!
}
if(copy)
cur = copy->next; //其实也可以不需要判断copy是否为空!(链表节点总数是偶数,所以cur只会是偶节点,copy不会为空!)
}
四、代码
//没必要弄清原链表的random指针指向哪个节点,只需要弄清原理即可!
// 需要注意:malloc开辟新空间
struct Node* copyRandomList(struct Node* head) {
//1.拷贝节点插入到原节点后面、
struct Node* cur = head; //没有哨兵位!
while (cur)
{
struct Node* copy = (struct Node*)malloc(sizeof(struct Node)); //每次都要开辟空间 //强制类型转换!
copy->val = cur->val; //赋值
//链接
struct Node* next = cur->next; //定义一个next指针,可不考虑顺序问题
cur->next = copy;
copy->next = next;
cur = next; //cur 指针在原链表往后走!
}
// 2.控制拷贝节点的random
cur = head; //重置cur
while (cur)
{
struct Node* copy = cur->next; //不需要额外开辟空间,copy节点已经连接了原节点
if (cur->random == NULL)
copy->random = NULL;
else //cur->random要注意是不是空!
copy->random = cur->random->next; //copy的random指向的是拷贝节点
//迭代 //copy不需要迭代,copy是封装在循环内的指针,由cur控制!
cur = copy->next; //cur 在原链表迭代!
}
//3.脱离原链表 : 尾插的思想
cur = head;
struct Node* CopyHead = NULL, *tail = NULL;
//尾插的思想链接节点!
while (cur)
{
struct Node* copy = cur->next;
//脱离
if(CopyHead == NULL)
{
CopyHead = tail = copy; //tail是尾节点!而不是尾节点的下一个!
}
else
{
tail->next = copy;
tail = tail->next; //tail是尾节点!而不是尾节点的下一个!
}
if(copy)
cur = copy->next; //其实也可以不需要判断copy是否为空!(链表节点总数是偶数,所以cur只会是偶节点,copy不会为空!)
}
return CopyHead;
}
五、附加
头插也是一种常用的思想,常用来逆置,讲解如下:
题目:反转链表(头插与非头插)-CSDN博客