算法题解记录27+++随机链表的复制(百日筑基)

2024-06-02 00:52

本文主要是介绍算法题解记录27+++随机链表的复制(百日筑基),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

一、题目描述:

        题目难度:中等

        给你一个长度为 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
  • -10^4 <= Node.val <= 10^4
  • Node.random 为 null 或指向链表中的节点。

二、解题准备

        1.了解题意:

        题目要求拷贝链表,需要注意的是,本题链表的定义和普通链表不同,本题链表的节点,除了val域和next域,还有一个random域,随机指向本链表的某一节点【但是这链表肯定不是环形链表】

        深度拷贝,要求复制出一个一模一样的链表,这种链表,除了val域一样,next域指向新节点外,random域指向的节点,不能是原先链表的节点。

        用3元式表示一个节点【val,next,random】,其中,@x表示指向第x个节点,这个x从0开始。

        举个例子,我们有一个链表【1,@1,@NULL】,【4,@2,@3】,【10,@3,@0】,【7,@NULL,@NULL】

        这个链表类似于下图【不懂绘图,勉强看吧】

        我们需要的新链表,要与这个一模一样,但是是下图这样的:

        至此你应该理解了题意。

        

        2.基本操作:

        本题涉及链表的创建和增加。

        3.基础原理:

        对于链表题目,要记住最基础的一个方法(如何遍历链表):

        如果是使用迭代while,那么,基础的迭代语法就是:

Node temp = head; // 防止遍历结束后,丢失读取链表的方法
while(temp!=null){print(temp.val); // 伪代码,访问temp的数据temp = temp.next;
}
// 结束条件:最终temp指向第一个null“节点”

三、解题思路

        首先考虑,如果去除random域,那么如何复制一份链表?

        1.简化思路:最基本的链表拷贝方法

        拷贝一个链表,其实是拷贝链表的val域,以及链表节点之间的相互关系。

        比如上面说的例子【1,4,10,7】。我们知道链表间的关系就是1指向4,4指向10,10指向7。

        由于链表的next域自带这种联系方式,所以拷贝的时候,不必把相互关系存储起来,而是在遍历时,一边拷贝即可。

        也就是说,我们首先创建一个对象res,指向head(或者用哑节点也行,无所谓)

        然后,遍历原链表,同时不断创建新结点。

        代码如下:

        Node res = new Node(head.val); // 指向头节点temp = head.next; // 如果指向head,那么while中会拷贝len次,而res已经拷贝一次real = res; // 指向头节点,避免丢失res// 一直迭代到temp指向第一个null节点。while(temp!=null){// 为什么用next?因为如果用real本身,会丢失联系。// 解释:real原先是一个null节点,你new之后,前面的联系就断了real.next = new Node(temp.val);temp = temp.next;real = real.next;}return res; // res就是我们要的答案

        得到拷贝基础链表的方法后,开始考虑本题的random域链表。

        我们首先要确定一件事:

        拷贝一个数据结构,就是拷贝元素值,以及元素之间的相互关系。

        如果进行一遍“简化思路”,那么,除了random域的元素关系,其它的元素关系都被拷贝了。

        所以,只需要考虑如何把random域的元素关系拷贝即可。

        2.思路:存储random域的元素关系

        如题,由于random是随机指向某个节点的,而链表不支持随机访问,也不能知道某个节点,在整个链表里,排在第几个元素。

        所以,我们需要用一个结构,将链表节点之间联系起来。

        比较简单地,自定义一个Class,有一个Node和一个int属性,头节点head是0,之后的按顺序排序。

        这里有一个问题:我们可以知道原链表的节点关系,但是无法映射到新链表中。

        解释:新链表是新对象,占用新内存,所以与原节点不能用“==”判同。

        因此,这个Class类,需要有两个Node、一个int属性,与上述类似,不过此时,两个Node,一个存储原链表的节点,另一个存储现在链表的节点。【在后续应用中,甚至不用int属性】

        此时,拷贝random域有方法了。解释:

        我们在浅度拷贝(即没考虑random域)时,将Class类填满。

        然后进行第二次遍历,这次遍历,从head开始,把原链表的每一个节点的random域找到,然后在Class中对应的新Node,即可深度拷贝。

        新问题:这种Class结构,需要花费很多资源,遍历起来也比较麻烦。

        解决方案:用HashMap。

四、解题难点分析

        无。

五、代码【HashMap】

class Solution {public Node copyRandomList(Node head) {// 空节点直接返回if(head == null){return null;}// 存储新、旧节点间关系Map<Node, Node> maps = new HashMap<>();Node temp = head.next;Node res, real;res = new Node(head.val);res.next = head.next;// 浅度拷贝real = res;maps.put(head, res);while(temp!=null){real.next = new Node(temp.val);maps.put(temp, real.next);temp = temp.next;real = real.next;}// 拷贝random域temp = head;real = res;while(temp!=null){if(temp.random != null){// 非空,则通过原链表的random域,映射到新链表的random域real.random = maps.get(temp.random);}temp = temp.next;real = real.next;}return res;}
}

        已经很久没更新百日筑基的内容了,一方面是这段时间忙于实训,需要重新学习、复习很多基础的框架知识,另一方面,算法题的刷题出现了瓶颈,对于回溯算法、贪心算法和图论,我已经觉得有点力不从心,虽然这些题目不难,但是由于自身知识储备不足,学起来比较吃力。

        不过好在,我已经大概刷了60余题,写百日筑基系列的基础,至少也到了下一个月,所以还是不担心缺少内容创作。这段文字写得很乱,也不知道有没有人在看这个系列,不过我会坚持下去的,积累就是人生漫漫长路的解决方案啊。

以上内容即我想分享的关于力扣热题27的一些知识。

        我是蚊子码农,如有补充,欢迎在评论区留言。个人也是初学者,知识体系可能没有那么完善,希望各位多多指正,谢谢大家。

这篇关于算法题解记录27+++随机链表的复制(百日筑基)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1022591

相关文章

C#文件复制异常:"未能找到文件"的解决方案与预防措施

《C#文件复制异常:未能找到文件的解决方案与预防措施》在C#开发中,文件操作是基础中的基础,但有时最基础的File.Copy()方法也会抛出令人困惑的异常,当targetFilePath设置为D:2... 目录一个看似简单的文件操作问题问题重现与错误分析错误代码示例错误信息根本原因分析全面解决方案1. 确保

MySQL 临时表与复制表操作全流程案例

《MySQL临时表与复制表操作全流程案例》本文介绍MySQL临时表与复制表的区别与使用,涵盖生命周期、存储机制、操作限制、创建方法及常见问题,本文结合实例代码给大家介绍的非常详细,感兴趣的朋友跟随小... 目录一、mysql 临时表(一)核心特性拓展(二)操作全流程案例1. 复杂查询中的临时表应用2. 临时

Java集合中的链表与结构详解

《Java集合中的链表与结构详解》链表是一种物理存储结构上非连续的存储结构,数据元素的逻辑顺序的通过链表中的引用链接次序实现,文章对比ArrayList与LinkedList的结构差异,详细讲解了链表... 目录一、链表概念与结构二、当向单链表的实现2.1 准备工作2.2 初始化链表2.3 打印数据、链表长

MySQL实现多源复制的示例代码

《MySQL实现多源复制的示例代码》MySQL的多源复制允许一个从服务器从多个主服务器复制数据,这在需要将多个数据源汇聚到一个数据库实例时非常有用,下面就来详细的介绍一下,感兴趣的可以了解一下... 目录一、多源复制原理二、多源复制配置步骤2.1 主服务器配置Master1配置Master2配置2.2 从服

MySQL配置多主复制的实现步骤

《MySQL配置多主复制的实现步骤》多主复制是一种允许多个MySQL服务器同时接受写操作的复制方式,本文就来介绍一下MySQL配置多主复制的实现步骤,具有一定的参考价值,感兴趣的可以了解一下... 目录1. 环境准备2. 配置每台服务器2.1 修改每台服务器的配置文件3. 安装和配置插件4. 启动组复制4.

基于Spring Boot 的小区人脸识别与出入记录管理系统功能

《基于SpringBoot的小区人脸识别与出入记录管理系统功能》文章介绍基于SpringBoot框架与百度AI人脸识别API的小区出入管理系统,实现自动识别、记录及查询功能,涵盖技术选型、数据模型... 目录系统功能概述技术栈选择核心依赖配置数据模型设计出入记录实体类出入记录查询表单出入记录 VO 类(用于

java中pdf模版填充表单踩坑实战记录(itextPdf、openPdf、pdfbox)

《java中pdf模版填充表单踩坑实战记录(itextPdf、openPdf、pdfbox)》:本文主要介绍java中pdf模版填充表单踩坑的相关资料,OpenPDF、iText、PDFBox是三... 目录准备Pdf模版方法1:itextpdf7填充表单(1)加入依赖(2)代码(3)遇到的问题方法2:pd

使用Go实现文件复制的完整流程

《使用Go实现文件复制的完整流程》本案例将实现一个实用的文件操作工具:将一个文件的内容完整复制到另一个文件中,这是文件处理中的常见任务,比如配置文件备份、日志迁移、用户上传文件转存等,文中通过代码示例... 目录案例说明涉及China编程知识点示例代码代码解析示例运行练习扩展小结案例说明我们将通过标准库 os

Zabbix在MySQL性能监控方面的运用及最佳实践记录

《Zabbix在MySQL性能监控方面的运用及最佳实践记录》Zabbix通过自定义脚本和内置模板监控MySQL核心指标(连接、查询、资源、复制),支持自动发现多实例及告警通知,结合可视化仪表盘,可有效... 目录一、核心监控指标及配置1. 关键监控指标示例2. 配置方法二、自动发现与多实例管理1. 实践步骤

python生成随机唯一id的几种实现方法

《python生成随机唯一id的几种实现方法》在Python中生成随机唯一ID有多种方法,根据不同的需求场景可以选择最适合的方案,文中通过示例代码介绍的非常详细,需要的朋友们下面随着小编来一起学习学习... 目录方法 1:使用 UUID 模块(推荐)方法 2:使用 Secrets 模块(安全敏感场景)方法