【算法】力扣:复制含有随机指针节点的链表

前置知识

  • 数据结构-链表:解法二采用了存粹的链表知识和特殊处理,最优解。
  • [可选]数据结构-哈希表:解法一使用了Java语言内置的哈希表
  • 【兴趣】哈希思想, 设计哈希函数,开放寻址法,数组模拟哈希表。–笔者未写此法, 如果读者是哈希表的忠实信徒可以尝试写一下, 或无脑开一个大数组避免冲突能够过所有测试用例。

强调, 链表在算法设计其实是练习coding速度的, 笔试应该优先采取容器的方式加快速度, 面试必须采用最优解法

题目:带随机指针的复制列表

  • 题目给定了Node类的定义。
class Node {
    int val;
    Node next;
    Node random;

    public Node(int val) {
        this.val = val;
        this.next = null;
        this.random = null;
    }
}

Node类中的value是节点值, next指针是常规链表的后继节点,不过, 题目还给节点附带了random指针,它随机指向链表中的某个节点,或可能指向为空(null)。

题目要求,我们深拷贝这个链表。

对于自定义类型,引用存储的是对象的地址, 不能简单的浅拷贝(值复制)这些节点, 因为从底层上相当于位的拷贝, 实际两个引用(指针)指向了同一的内存块。怎么做? 单独开一块内存,基本类型进行值拷贝,自定义类型进行深拷贝。

假设节点node.val = 1,那么应该重新创建新节点newNode,它的值也是1,但在不同的堆内存。
这道题,应该避免新列表中的任何指针都不应指向原始列表中的节点。
它们应该在内存中独立,而不是同一块, 而且它们对应的字段应该保持一致。
本质是Java容器的clone拷贝

解法1:Java内置HashMap

为了新链表与原链表建立一个直观的联系。
采用哈希表

  1. 遍历原链表,将原链表节点与新链表节点(创建一份副本)映射联系起来, 放入HashMap中。
  2. 再次遍历原链表,根据key-value的对应关系,取出对应节点的副本,将next,random对应原链表节点的副本连接起来。
import java.util.HashMap;

//不要提交这个类
class Node {
	int val;
	Node next;
	Node random;

	public Node(int val) {
		this.val = val;
		this.next = null;
		this.random = null;
	}
}
//测试链接:https://leetcode.com/problems/copy-list-with-random-pointer/submissions/1426261747/
//提交:将类名改为Solution
public class CopyRandomList {
	public static Node copyRandomList(Node head) {
		if (head == null) {
			return null;//排除空表, 直接返回null
		}
		//初始化哈希表: key:原链表的节点 value:对应节点的副本
		HashMap<Node, Node> map = new HashMap<>();
		Node cur;
		//第一次遍历为每个节点拷贝一份副本, 存储到哈希表
		for (cur = head; cur != null; cur = cur.next) {
			map.put(cur, new Node(cur.val));// 拷贝原节点的一份副本。
		}
		//新链表的头节点和尾节点
		Node newHead = map.get(head);
		Node newTail = newHead;
		//第二次遍历原链表,连接指针。
		for (cur = head; cur != null; cur = cur.next) {
			//根据哈希表取出节点next,random的副本
			newTail.next = map.get(cur.next);
			newTail.random = map.get(cur.random);
			newTail = newTail.next;//尾节点往后跟。
		}
		return newHead;//返回头节点。
	}
}

在这里插入图片描述
时间复杂度: O ( n ) O(n) O(n), 因为只遍历了两次原链表,哈希表插入查询是常量时间。
空间复杂度: O ( n ) O(n) O(n), 因为存储了所有节点数及其副本。

解法2:技巧

接下来是进阶方法, 空间复杂度要做到 O ( 1 ) O(1) O(1)
前面的哈希表的作用?
存储原节点和副本的联系。通过哈希表, 原链表的连接方式可以很好映射到新链表的处理。
有没有同样的方法, 可以做到常数空间(不用哈希表)也能实现高效的寻址映射呢?
单链表有个很快的特性,已知节点可以迅速找到它的后继,因为只需要node.next即可。
基本思路:

  1. 遍历原链表,并将每个节点生成的副本挂在该节点的后面,来达到快速查询的作用。
    比如原链表[1->2->3->null],这里以1'视作节点1的副本, 按照上述。应该拷贝原节点并修改原链表[1->1'->2->2'->3->3'->null]
  2. 上述可以快速寻址, 但是有什么显然的意义呢?答:修改random指针,比如1的random指针指向3这个节点,那么1’应该指向3’。那么再次遍历原链表修改random指针时。比如修改1‘的random指针,先通过1.random定位到3, 再通过3.next定位到3’, 再让1‘.random连接到3’。总结:第二次遍历根据副本是源节点的后继来设置副本的random指针。
  3. 最后一步,由于我们修改了原链表,还要获得新链表。因此,需要实现链表分离。
public static Node copyRandomList(Node head) {
    // 单独处理空链表的情况,直接返回null
    if (head == null) {
        return null;
    }

    // 第一次遍历链表:为每个节点创建一个副本,并将副本插入到原节点的后面
    Node cur = head, next = null;
    while (cur != null) {
        next = cur.next; // 暂存原节点的下一个节点
        // 创建副本节点,并插入到原节点的后面
        cur.next = new Node(cur.val); 
        cur.next.next = next; // 将新创建的副本节点的next指向原节点的下一个节点
        cur = next; // 移动到原链表的下一个节点
    }

    // 第二次遍历链表:设置新节点的random指针
    cur = head;
    Node curCopy = null;
    while (cur != null) {
        next = cur.next.next; // 跳过副本节点,找到下一个原节点
        curCopy = cur.next; // cur的副本节点
        // 如果原节点的random指针不为空,设置副本节点的random指针指向原链表random节点的副本
        curCopy.random = cur.random == null ? null : cur.random.next;
        cur = next; // 移动到下一个原节点
    }

    // 第三次遍历链表:将原链表和副本链表分离
    cur = head;
    Node newHead = head.next; // 新链表的头节点
    while (cur != null) {
        next = cur.next.next; // 获取原链表的下一个节点(跳过副本节点)
        curCopy = cur.next; // 获取当前节点的副本
        // 恢复原链表的结构:将原节点的next指针指回到下一个原节点
        cur.next = next;
        // 连接副本链表的next指针:指向下一个副本节点
        curCopy.next = next == null ? null : next.next; // 处理next为null的情况
        cur = next; // 移动到下一个原节点
    }

    // 返回新链表的头节点
    return newHead;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/894215.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

RabbitMQ异常

一、如果遇到tags[[]], channelnull, acknowledgeModeMANUAL local queue size0&#xff1b;An unexpected connection driver error occured这样类似的错误&#xff0c;请检查你的host配置是否正确,是否与配置文件中的相同 2024-10-17 08:10:46.053 INFO 18988 --- [tContai…

2023年华为杯数学建模竞赛题F论文和代码

强对流降水临近预报建模与优化 对问题一&#xff0c;为了实现基于前一小时&#xff08;10帧&#xff09;的实测雷达观测量&#xff08;ZH、ZDR、KDP&#xff09;&#xff0c;对后续一小时&#xff08;10帧&#xff09;的ZH进行预报&#xff0c;本文首先建立了线性拟合与RMSE双驱…

网络通信与并发编程(二)基于tcp的套接字、基于udp的套接字、粘包现象

基于tcp的套接字 文章目录 基于tcp的套接字一、套接字的工作流程二、基于tcp的套接字通信三、基于udp的套接字通信四、粘包现象 一、套接字的工作流程 Socket是应用层与TCP/IP协议族通信的中间软件抽象层&#xff0c;它是一组接口。在设计模式中&#xff0c;Socket其实就是一个…

Linux基本使用和程序部署

文章目录 一. Linux背景Linux发行版 二. Linux环境搭建Linux常见命令lspwdcdtouchcatmkdirrmcpmvtailvimgreppsnetstat管道 三. 搭建java部署环境安装jdk安装mysql部署Web项目到Linux 一. Linux背景 1969−1970年,⻉尔实验室的DennisRitchie和KenTompson开发了Unix操作系统. 他…

链动2+1芸众商城421+全插件独立版源码

芸众商城最新全插件421个&#xff0c;去授权 源码全开源链动21商城小程序 这套版本插件全部都是新版本&#xff0c;并非外面那种老版本 老插件全部都不能用的&#xff0c;一堆bug问题&#xff0c;我们插件源码是直接打官方授权源码所以都是最新的&#xff0c;还有很多小程序前…

three.js 使用geojson ,实现中国地图区域,边缘流动效果

three.js 使用geojson &#xff0c;实现中国地图区域&#xff0c;边缘流动效果 在线链接&#xff1a;https://threehub.cn/#/codeMirror?navigationThreeJS&classifyexpand&idgeoBorder 国内站点预览&#xff1a;http://threehub.cn github地址: https://github.co…

android openGL ES详解——混合

一、混合概念 混合是一种常用的技巧&#xff0c;通常可以用来实现半透明。但其实它也是十分灵活的&#xff0c;你可以通过不同的设置得到不同的混合结果&#xff0c;产生一些有趣或者奇怪的图象。混合是什么呢&#xff1f;混合就是把两种颜色混在一起。具体一点&#xff0c;就…

Java线程池的几个重要核心参数

一、corePoolSize&#xff08;核心线程数&#xff09; 含义&#xff1a;线程池中始终保持存活的线程数量。作用&#xff1a;当有新任务提交时&#xff0c;如果线程池中线程数量小于核心线程数&#xff0c;会创建新线程来执行任务。即使这些线程处于空闲状态&#xff0c;它们也…

03 django管理系统 - 部门管理 - 部门列表

部门管理 首先我们需要在models里定义Dept类 # 创建部门表 class Dept(models.Model):name models.CharField(max_length100)head models.CharField(max_length100)phone models.CharField(max_length15)email models.EmailField()address models.CharField(max_length2…

【数据采集工具】Flume从入门到面试学习总结

国科大学习生活&#xff08;期末复习资料、课程大作业解析、大厂实习经验心得等&#xff09;: 文章专栏&#xff08;点击跳转&#xff09; 大数据开发学习文档&#xff08;分布式文件系统的实现&#xff0c;大数据生态圈学习文档等&#xff09;: 文章专栏&#xff08;点击跳转&…

进入 Searing-66 火焰星球:第一周游戏指南

Alpha 第四季已开启&#xff0c;穿越火焰星球 Searing-66&#xff0c;带你开启火热征程。准备好勇闯炙热的沙漠&#xff0c;那里有无情的高温和无情的挑战在等待着你。从高风险的烹饪对决到炙热的冒险&#xff0c;Searing-66 将把你的耐力推向极限。带上充足的水&#xff0c;天…

【热门】软件管理系统erp,研+产+供+销+业+财+数据一体

随着科技的进步,原有农业种植方式已经不能满足社会发展的需要,必须对传统的农业进行技术更新和改造。经过多年的实践,人们总结出一种新的种植方法——温室农业,即“用人工设施控制环境因素,使作物获得最适宜的生长条件,从而延长生产季节,获得最佳的产出”。这种农业生产方式…

【mod分享】极品飞车10卡本峽谷白日mod,在白天竞速也是一种很棒的体验,更多的车辆,更高清的材质,更棒的灯光效果、同样光追

各位好&#xff0c;今天小编给大家带来一款新的高清重置魔改MOD&#xff0c;本次高清重置的游戏叫《极品飞车10卡本峡谷》。 《极品飞车10&#xff1a;卡本峡谷》继承了前几款游戏的开放式环境的特点&#xff0c;并且在此基础上做出了很大的改进。这次玩家仍旧要开着车在城市里…

游戏逆向基础-找释放技能CALL

思路&#xff1a;通过send断点然后对send的data参数下写入断点找到游戏里面的技能或者攻击call 进入游戏先选好一个怪物&#xff08;之所以要先选好是因为选怪也会断&#xff0c;如果直接左键打怪的话就会断几次&#xff09; 断下来后对参数下硬件写入断点 硬件断点断下来后先…

ubuntu下安装mysql遇到的问题

ubuntu下安装mysql sudo apt install -y mysql-server 出现问题 ……by process 3455 解决 安装 启动 systemctl status mysql.service sudo mysql -u root -p 如何修改密码 与datagrip的连接 查看IP ifconfig 若没安装 参考 Windows10的DataGrip2024.1.4连接ubuntu22.04中的M…

前端布局与响应式设计综合指南(三)

​&#x1f308;个人主页&#xff1a;前端青山 &#x1f525;系列专栏&#xff1a;Css篇 &#x1f516;人终将被年少不可得之物困其一生 依旧青山,本期给大家带来Css篇专栏内容:前端布局与响应式设计综合指南(三) 目录 42、px/em/rem有什么区别&#xff1f;为什么通常给font-s…

23种设计模式之工厂方法模式

文章目录 1. 简介2. 代码2.1 抽象类&#xff1a;Course.java2.2 产品A&#xff1a;JavaCourse.java2.3 产品B&#xff1a;PythonCourse.java2.4 工厂抽象类&#xff1a;CourseFactory.java2.5 产品A的工厂A&#xff1a;JavaCourseFactory.java2.6 产品B的工厂B&#xff1a;PyCo…

Java实现文件上传功能

目录 1、准备工作 2、注意事项 3、jsp页面代码 4、Servlet 5、注册Servlet 1、准备工作 导入依赖&#xff1a;commons-fileupload和commons-io 2、注意事项 ①为保证服务器安全&#xff0c;上传文件应该放在外界无法直接访问的目录下&#xff0c;比如WEB-INF目录下 ②为…

力扣66~70题

题66&#xff08;简单&#xff09;&#xff1a; python代码&#xff1a; class Solution:def plusOne(self, digits: List[int]) -> List[int]:s_str.join([str(i) for i in digits])nstr(int(s_str)1)n_strlist(n)res[int(i) for i in n_str]return res题67&#xff08;简…

Java项目-基于Springboot的智慧养老平台项目(源码+文档).zip

作者&#xff1a;计算机学长阿伟 开发技术&#xff1a;SpringBoot、SSM、Vue、MySQL、ElementUI等&#xff0c;“文末源码”。 开发运行环境 开发语言&#xff1a;Java数据库&#xff1a;MySQL技术&#xff1a;SpringBoot、SpringClud、Vue、Mybaits Plus、ELementUI工具&…