没有关系的话,那就去建立关系吧
创始人
2025-05-29 14:22:34

        今天给大家分享一道链表的好题--链表的深度拷贝,学会这道题,你的链表就可以达到优秀的水平了。力扣

         先来理解一下题目意思,即建立一个新的单向链表,里面每个结点的值与对应的原链表相同,并且random指针也要指向新链表中与原链表对应的那个相对位置。(即假设原链表中的第一个结点的random指向原链表的最后一个结点,那么新链表的第一个结点也要指向新链表的最后一个结点,即random指针是链表内部确定相对位置的一个指针)。

        首先,拷贝一个新的链表,其对应结点的值与原链表对应结点的值相同是很容易实现的。可以用一个cur指针遍历原链表,然后建立一个新链表头,然后逐个尾插既可。   

struct Node* cur=head;struct Node* newhead = NULL;struct Node* tail = NULL;while(cur){//每次尾插都需要一个新结点,其val与原链表对应相等struct Node* newnode = (struct Node*)malloc(sizeof(struct Node));//第一次尾插时if(NULL ==tail){newhead = tail =newnode;newnode->val = cur->val;newnode->next = newnode->random = NULL;}//后续尾插else{tail->next = newnode;tail = tail->next;}//拷贝一个新结点后,cur往后走cur = ucr->next;}

此时,只是完成了next链接和val拷贝,random的指向还没有拷贝。

暴力求解O(N^2)

可以建立一个结构体的指针数组   struct Node* arr[n]  n为原链表中的结点数

    struct Node* arr[n];int count = 0;while(cur){arr[count] = cur->random;count++;cur =cur->next;}

再次利用cur遍历原链表,将每个结点的random保存在创建的结构体指针数组 arr中。

struct Node* newcur=newhead;int newcount=-1;while(newcur){++newcount;//每次进来都拿到原链表的一个randomstruct Node* tmp = arr[newcount];//用tmp保存这个randomcur = head;while(cur != tmp){//遍历原链表,看看此时的random是原链表的第几个结点count++;}//找到新链表中对应的第count个结点struct Node* find = newhead;while(count--){//一共走count步newhead = newhead->next;}//找到了newcur位置的random的指向newcur->random = find;//newcur继续往后走newcur = newcur->next;}

暴力解法虽然也能解决问题,但是时间复杂度为O(N^2),效率低,不推荐。

更优解O(N)

        通过暴力解法我们可以发现,寻找random指向的难点在于它是随机的,如果想要确定具体的相对位置(相对于头是第几个)则必须经过2次遍历,那么怎样简化寻找相对位置的过程呢?

        想一下random的关系在哪里出现,应该只有原链表中,而我们又想要建立新链表中random的关系,因此我们必须建立原链表与新链表直接的关系,通过原链表的random找到新链表的random。

        再借助next指针思考,我们可以将新链表对应的结点连接到原链表上。

        

 此时逻辑一下子清晰了,每个新结点都在原对应结点的next位置。

例如:对于13这个结点的random连接,新13->random = 原13->random->next,即通过原链表random的查找方式,再加上next,来连接新链表的random。

具体的实现过程分为3个方面。

1、连接原、新链表

struct Node* cur=head;while(cur){//建立新结点并初始化struct Node* newnode = (struct Node*)malloc(sizeof(struct Node));newnode->next = newnode->random =NULL;random->val = cur->val;//先保存一下原结点的下一个结点struct Node* next = cur->next;//将新结点连接到原链表cur->next = newnode;newnode->next = next;//cur继续往后走cur = next;}

2、建立新链表的random联系

    cur = head;while(cur){//确保cur不为NULL,再建立copy指向cur的nextstruct Node* copy = cur->next;//建立copy的random联系时,要确保其不为空,否则不能进行next操作//因此这里讨论一下原链表的random是否为空if(NULL == cur->random){copy->random = NULL;}else{   copy-> random = cur->random->next;}//连接后cur继续往后走cur = copy->next;}

要注意,copy指针和cur指针移动的位置,可以理解为cur不为空时,建立copy指向此时cur的下一个,完成相关连接后copy丢弃,cur往后走,copy只起到临时变量的作用(连接后便丢弃)。

 

3、分离原、新链表

分离的过程直接将copy部分的结点尾插到一个新结点即可

struct Node* newhead=NULL,*tail=NULL;cur=head;while(cur){struct Node* copy = cur->next;struct Node* next = copy->next;if(NULL == tail)//第一次尾插{newhead = tail =copy;}else//后续尾插{tail->next = copy;//tail往后走,指向新的最后一个结点tail = tail->next;}//分离原链表,cur继续往后走cur->next=next;cur= next;}return newhead;

这部分要注意,else内部tail往下走是后续尾插才有的操作。

 

总结:为了优化代码,使时间复杂度变为O(N),必须建立原来链表的新链表直接的联系,借助原链表的random->next,来连接新链表的random。

所以说,没有关系的话,那就去勇敢的建立关系吧。

 

相关内容

热门资讯

开发还分前、后端?它们都是做什... 开发还分前、后端?它们都是做什么的? 2023-03-20 14:54·...
Nordic nRF5 mes... nRF5 mesh蓝牙组网软件SDK下载链接: NordicSemiconductor...
【uniapp tabs标签组... 前言 这个tabs功能是很多移动端项目都要用的 最近我刚好遇到了这个功能 因为我们项目不让用uvie...
电子采购管理软件开发功能有哪些... 电子采购系统是将供应商、招标机构、评标专家、政府监督机构等连接起来,企业、机关和个人在...
K8S集群1.24使用dock... 文章目录1. 环境介绍2. 异常信息3. 分析问题3.1 kubernetes 健康检查3.1.1 ...
TS接口类型 40. TS接口 1. 定义 TypeScript 中的接口是一种抽象结构,用于定义对...
Time out. EFI N... 背景:最近使用了虚拟机,正准备安装个Windows10的操作系统...
数字电路2. OC门、OD门、... 数字电路2. OC门、OD门、三态门一、OC门——集电集开路门1. 基本概念2. 作用3. 使用要点...
操作系统性能优化实践 感谢内容提供者:四川省奇呱科技有限公司 文章目录一、常见性能指标及USE法分类1.C...
展现AI与自动化测试技术之间的... 目录:导读 前言 一、介绍 1、什么是自动化测试技术 2、痛点 3、几款优秀的自动化测...
第一周web 目录 [NISACTF 2022]popchains  [NSSCTF 2022 Spring Re...
百元降噪耳机推荐,适合学生党入... ​降噪蓝牙耳机怎么选?有哪些适合学生党使用的百元降噪蓝牙耳机?很多人在面...
C# winform坐标系类型... C# winform坐标系类型详解 GDI+ 使用三个坐标空间:世界、页面和设...
Windows平台安装MacO... 写在前面:博主是一只经过实战开发历练后投身培训事业的“小山猪”,昵称取自...
Windos远程连接Linux... ssh安装 使用root用户登录 su root 更换apt 下载源为清华源,先备...
近期媒体邀约活动总结,注意事项 传媒如春雨,润物细无声,大家好,我是51媒体网胡老师。 随...
每日一博 - Java 异步编... 文章目录概述概述Executor与线程池Java 中的线程池使用线程池的注意事项强烈建议使用有界队列...
thinkphp基础学习 Composer安装thinkphp6,输入命令,其中tp为项目目录名可...
10从零开始学Java之开发J... 作者:孙玉昌,昵称【一一哥】,另外【壹壹哥】也是我哦CSD...
项目团队任务分配的5大注意事项         想要把工作合理分配给下属,在进行项目团队任务分配时,需要...
scratch猜数字游戏 电子... 目录 scratch猜数字游戏 一、题目要求 1、准备工作 2、功能实现 二、案例分析 <
Windows逆向安全(一)之... C语言内联汇编和调用协定 前面我们通过分析反汇编分析了C语言,现在我们来探究如何在C语...
「操作系统」什么是用户态和内核... 「操作系统」什么是用户态和内核态?为什么要区分 参考&鸣谢 从根上理解用户态与内核态...
vue项目局域网前后端接口对接... 场景: 项目开发中,当前没有服务器,或感觉每次部署包麻烦的...
选择器(设置样式的元素) 系列文章目录 前端系列文章——传送门 CSS系列文章——传送门 文章目录系列文章目录1.基本选择器...
【IoT】嵌入式Linux开发... 目录 LCD类型 分辨率 色深(色位) 尺寸 PPI(pixels per inch) LCD连接...
MySQL事务处理 Java知识点总结:想看的可以从这里进入 目录4、MySQL事务处理4.1、 简介4...
MyBatis级联一对一与一对... 级联 MyBatis 的级联分为3 种: 鉴别器(discriminator):它是一...
栈和队列(stack和queu... 目录一、栈1.1 什么是栈?1.2 栈的相关操作1.2.1 结构体变量的声明1.2.2...
Hive 文章目录1️⃣、Hive入门1.1、什么是Hive1.2、Hive架构2️⃣、Hive安装及使用2....