算法题解记录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

相关文章

Java使用SLF4J记录不同级别日志的示例详解

《Java使用SLF4J记录不同级别日志的示例详解》SLF4J是一个简单的日志门面,它允许在运行时选择不同的日志实现,这篇文章主要为大家详细介绍了如何使用SLF4J记录不同级别日志,感兴趣的可以了解下... 目录一、SLF4J简介二、添加依赖三、配置Logback四、记录不同级别的日志五、总结一、SLF4J

在Spring Boot中浅尝内存泄漏的实战记录

《在SpringBoot中浅尝内存泄漏的实战记录》本文给大家分享在SpringBoot中浅尝内存泄漏的实战记录,结合实例代码给大家介绍的非常详细,感兴趣的朋友一起看看吧... 目录使用静态集合持有对象引用,阻止GC回收关键点:可执行代码:验证:1,运行程序(启动时添加JVM参数限制堆大小):2,访问 htt

openCV中KNN算法的实现

《openCV中KNN算法的实现》KNN算法是一种简单且常用的分类算法,本文主要介绍了openCV中KNN算法的实现,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的... 目录KNN算法流程使用OpenCV实现KNNOpenCV 是一个开源的跨平台计算机视觉库,它提供了各

MySQL 中查询 VARCHAR 类型 JSON 数据的问题记录

《MySQL中查询VARCHAR类型JSON数据的问题记录》在数据库设计中,有时我们会将JSON数据存储在VARCHAR或TEXT类型字段中,本文将详细介绍如何在MySQL中有效查询存储为V... 目录一、问题背景二、mysql jsON 函数2.1 常用 JSON 函数三、查询示例3.1 基本查询3.2

springboot+dubbo实现时间轮算法

《springboot+dubbo实现时间轮算法》时间轮是一种高效利用线程资源进行批量化调度的算法,本文主要介绍了springboot+dubbo实现时间轮算法,文中通过示例代码介绍的非常详细,对大家... 目录前言一、参数说明二、具体实现1、HashedwheelTimer2、createWheel3、n

Python获取中国节假日数据记录入JSON文件

《Python获取中国节假日数据记录入JSON文件》项目系统内置的日历应用为了提升用户体验,特别设置了在调休日期显示“休”的UI图标功能,那么问题是这些调休数据从哪里来呢?我尝试一种更为智能的方法:P... 目录节假日数据获取存入jsON文件节假日数据读取封装完整代码项目系统内置的日历应用为了提升用户体验,

Spring Boot 配置文件之类型、加载顺序与最佳实践记录

《SpringBoot配置文件之类型、加载顺序与最佳实践记录》SpringBoot的配置文件是灵活且强大的工具,通过合理的配置管理,可以让应用开发和部署更加高效,无论是简单的属性配置,还是复杂... 目录Spring Boot 配置文件详解一、Spring Boot 配置文件类型1.1 applicatio

Python中随机休眠技术原理与应用详解

《Python中随机休眠技术原理与应用详解》在编程中,让程序暂停执行特定时间是常见需求,当需要引入不确定性时,随机休眠就成为关键技巧,下面我们就来看看Python中随机休眠技术的具体实现与应用吧... 目录引言一、实现原理与基础方法1.1 核心函数解析1.2 基础实现模板1.3 整数版实现二、典型应用场景2

SpringBoot实现MD5加盐算法的示例代码

《SpringBoot实现MD5加盐算法的示例代码》加盐算法是一种用于增强密码安全性的技术,本文主要介绍了SpringBoot实现MD5加盐算法的示例代码,文中通过示例代码介绍的非常详细,对大家的学习... 目录一、什么是加盐算法二、如何实现加盐算法2.1 加盐算法代码实现2.2 注册页面中进行密码加盐2.

MySQL INSERT语句实现当记录不存在时插入的几种方法

《MySQLINSERT语句实现当记录不存在时插入的几种方法》MySQL的INSERT语句是用于向数据库表中插入新记录的关键命令,下面:本文主要介绍MySQLINSERT语句实现当记录不存在时... 目录使用 INSERT IGNORE使用 ON DUPLICATE KEY UPDATE使用 REPLACE