复制带随机指针的链表(精美图示详解哦)

在这里插入图片描述

引言

前面我们了解了关于单链表与带头双向循环链表,相信大家对于链表的知识已经有所掌握了,在本篇文章中将继续介绍一道题目:复制带随机指针的链表。
复制带随机指针的链表OJ连接

复制带随机指针的链表

题目描述与思路

在这里插入图片描述
这道题目要求我们深度拷贝一种带随机指针的链表,该链表的结点由3个成员,分别是:数据val、指向下一个结点的指针next、指向某个随机结点的指针random:

struct Node 
{
	int val;
	struct Node* next;
	struct Node* random;
};

要求我们深度拷贝该链表,即在拷贝的链表中,不仅要拷贝每个链表中的数据然后用next将每个结点按顺序连接起来,还要实现拷贝的链表每个结点中的random指针指向的结点也与原链表相同。

比如有带随机指针的链表:
在这里插入图片描述
其中有5个结点,每个结点中有一个指针next指向下一个结点。同时有第1个结点的random指针指向NULL;第2个结点的random指针指向第1个结点;第3个结点的random指针指向第5个结点;第4个结点的random指针指向第3个结点;第5个结点的random指针指向第1个结点。
在我们复制出的链表中,也要具有这样的关系。

函数只有1个参数,即链表的头节点。结构体变量与主函数部分已经定义,我们只需要实现接口即可。并返回复制后的头结点。

对于这道题的实现,在这篇文章中将提供一种较为简单的思路:
先将链表每个结点复制一份放到原结点的后面。此时,每个原来的结点的next指针指向的都是复制后的它本身,每一个复制的结点的next指针指向的都是原该节点的下一个结点(最后一个指向的是NULL);
然后,我们通过遍历原链表,找到原链表每个结点的random指针指向的原结点,该节点的next成员指向的就是它的复制结点。将它与对应的新结点连接即可;
最后,将已经拷贝好的新结点从原链表上剥离再依次连接,并且复原原链表,返回值即可。

实现

为了使代码更简洁,我们可以对结构体名称重命名:

typedef struct Node Node;

为实现这个算法,我们首先创建3个指针变量:cur用于遍历原链表、next用于指向原链表中cur的下一个结点、copy用于指向复制的新链表的结点:

Node* cur = head;
Node* next = head;
Node* copy = NULL;

有了这些指针后,首先实现遍历原链表,拷贝每一个结点并将其放在原结点后:
while循环,当cur为空时循环结束。
每次循环中,首先将next改为cur->next使其指向原链表中next的下一个元素;然后动态开辟一块空间,用copy指向这块空间,并判断其是否成功开辟;然后将copy->val 改为 cur->val,即拷贝该原结点的值到新结点;然后将copy->next 改为 next,即连接新结点与该原结点的下一个结点;然后将cur->next 改为 copy,即连接该原结点与新结点;最后cur改为next,cur指向该原结点在原链表中的下一个结点:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
再遍历原链表,新结点的random指针指向的元素就是原结点的random的next指向的结点。当然,在遍历之前要先将cur的值改回头节点:
首先,将copy 改为 cur->next,即使copy指向由cur复制而来的结点;然后将next 改为 copy->next,即让next指向原链表中cur的下一个结点;然后判断cur->random是否为空,若为空就说明该原结点的random指针指向的是NULL,此时让对应新结点的random指针也指向NULL即可,若不为空,则新结点的random指针指向的元素就是原结点的random的next指向的结点,此时将copy->random 改为 cur->random->next即可,即连接copy与其random指向的结点;最后将cur改为next,即将cur向后移动到原链表的下一个结点:

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

最后,再遍历一遍链表,将新结点尾插到一个哨兵位的头结点上,然后恢复原链表:
我们可以动态开辟这个哨兵位的头结点,验证并初始化:

Node* copyguard = (Node*)malloc(sizeof(Node));
assert(copyguard);
copyguard->val = -1;
copyguard->random = NULL;
copyguard->next = NULL;

并且创建一个copytail变量用来始终指向链表的尾结点,初始化为头节点copyguard:

Node* copytail = copyguard;

在将cur的值改回头节点后就可以开始遍历了:
首先将copy 改为 cur->next,即使copy指向由cur复制而来的结点;然后将next 改为 copy->next,即让next指向原链表中cur的下一个结点后;然后将copytail->next 改为 copy,即将copy尾插到尾结点后;然后将copytail 改为 copy,即使copytail始终指向尾结点;然后将cur->next 改为 next,即恢复原链表中cur与其下一个结点的连接;然后将cur改为next,即将cur向后移动到原链表的下一个结点:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
最后,将copytail->next改为NULL,然后free释放哨兵位的头结点的空间后就可返回copyguard->next的值了。但是由于释放后就不能返回值,所以先用一个ret指针记录low->next的值,等释放low与above指向的空间后,返回ret即可:
在这里插入图片描述

struct Node* copyRandomList(struct Node* head)
{
	typedef struct Node Node;
	Node* cur = head;
	Node* next = head;
	Node* copy = NULL;
	while (cur)//把链表的每一个结点拷贝一份放在原结点的后面
	{
		next = cur->next;
		copy = (Node*)malloc(sizeof(Node));
		assert(copy);
		copy->val = cur->val;
		copy->next = next;
		cur->next = copy;
		cur = next;
	}
	cur = head;
	while (cur)//原结点的random指向的是原链表的任意指针指向的结点,该结点的next指向的就是该新结点的random的指向
	{
		copy = cur->next;
		next = copy->next;
		if (cur->random)
		{
			copy->random = cur->random->next;
		}
		else
		{
			copy->random = NULL;
		}
		cur = next;
	}
	//将新链表独立并将原链表连接
	Node* copyguard = (Node*)malloc(sizeof(Node));
	assert(copyguard);
	copyguard->val = -1;
	copyguard->random = NULL;
	copyguard->next = NULL;
	Node* copytail = copyguard;
	cur = head;
	while (cur)
	{
		copy = cur->next;
		next = copy->next;
		copytail->next = copy;
		copytail = copy;
		cur->next = next;
		cur = next;
	}
	copytail->next = NULL;
	Node* ret = copyguard->next;
	free(copyguard);
	return ret;
}

总结

到此,关于复制带随机指针的链表的一种解法就介绍完了。当然会有其他的算法解决,欢迎大家在评论区讨论

如果大家认为我对某一部分没有介绍清楚或者某一部分出了问题,欢迎大家在评论区提出

如果本文对你有帮助,希望一键三连哦

希望与大家共同进步哦

  • 14
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 15
    评论
评论 15
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

阿qiu不熬夜

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值