LeetCode 138. Copy List with Random Pointer【链表,DFS,迭代,哈希表】中等

news2024/10/6 14:33:10

本文属于「征服LeetCode」系列文章之一,这一系列正式开始于2021/08/12。由于LeetCode上部分题目有锁,本系列将至少持续到刷完所有无锁题之日为止;由于LeetCode还在不断地创建新题,本系列的终止日期可能是永远。在这一系列刷题文章中,我不仅会讲解多种解题思路及其优化,还会用多种编程语言实现题解,涉及到通用解法时更将归纳总结出相应的算法模板。

为了方便在PC上运行调试、分享代码文件,我还建立了相关的仓库:https://github.com/memcpy0/LeetCode-Conquest。在这一仓库中,你不仅可以看到LeetCode原题链接、题解代码、题解文章链接、同类题目归纳、通用解法总结等,还可以看到原题出现频率和相关企业等重要信息。如果有其他优选题解,还可以一同分享给他人。

由于本系列文章的内容随时可能发生更新变动,欢迎关注和收藏征服LeetCode系列文章目录一文以作备忘。

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

类似题目:

  • 133. 克隆图

本题要求我们对一个特殊的链表进行深拷贝。如果是普通链表,我们可以直接按照遍历的顺序创建链表节点。而本题中因为随机指针的存在,当我们拷贝节点时,「当前节点的随机指针指向的节点」可能还没创建,因此我们需要变换思路

解法1 递归+哈希表

一个可行方案是,我们利用回溯的方式,让每个节点的拷贝操作相互独立。对于当前节点,我们首先要进行拷贝,然后我们进行「当前节点的后继节点」和「当前节点的随机指针指向的节点」拷贝,拷贝完成后将创建的新节点的指针返回,即可完成当前节点的两指针的赋值

具体地,我们用哈希表记录每一个节点对应新节点的创建情况。遍历该链表的过程中,我们检查「当前节点的后继节点」和「当前节点的随机指针指向的节点」的创建情况。如果这两个节点中的任何一个节点的新节点没有被创建,我们都立刻递归地进行创建。当我们拷贝完成,回溯到当前层时,我们即可完成当前节点的指针赋值。

注意一个节点可能被多个其他节点指向,因此我们可能递归地多次尝试拷贝某个节点,为了防止重复拷贝,我们需要首先检查当前节点是否被拷贝过,如果已经拷贝过,我们可以直接从哈希表中取出拷贝后的节点的指针并返回即可。

在实际代码中,我们需要特别判断给定节点为空节点的情况。

class Solution {
public:
    unordered_map<Node*, Node*> cachedNode;
    Node* copyRandomList(Node* head) {
        if (head == nullptr) return nullptr;
        if (!cachedNode.count(head)) {
            Node* headNew = new Node(head->val);
            cachedNode[head] = headNew;
            headNew->next = copyRandomList(head->next);
            headNew->random = copyRandomList(head->random);
        }
        return cachedNode[head];
    }
};

复杂度分析:

  • 时间复杂度: O ( n ) O(n) O(n) ,其中 n n n 是链表的长度。对于每个节点,我们至多访问其「后继节点」和「随机指针指向的节点」各一次,均摊每个点至多被访问两次。
  • 空间复杂度: O ( n ) O(n) O(n) ,其中 n n n 是链表的长度。为哈希表的空间开销。

解法2 迭代+哈希表

先用一个循环把新旧链表对应的两个结点捆绑在一个二元组里,然后再用一个循环完成对新链表每个结点的 n e x t next next 域和 r a n d o m random random 域的赋值:

class Solution {
public:
    Node* copyRandomList(Node* head) {
        if (head == nullptr) return nullptr;
        unordered_map<Node*, Node*> cachedNode;
        
        Node* cur = head;
        while (cur) {
            cachedNode[cur] = new Node(cur->val);
            cur = cur->next;
        }
        cur = head;
        while (cur) {
            cachedNode[cur]->next = cachedNode[cur->next];
            cachedNode[cur]->random = cachedNode[cur->random];
            cur = cur->next;
        }
        return cachedNode[head];
    }
};

复杂度分析:

  • 时间复杂度: O ( n ) O(n) O(n) ,其中 n n n 是链表的长度。
  • 空间复杂度: O ( n ) O(n) O(n) ,其中 n n n 是链表的长度。为哈希表的空间开销。

解法3 迭代 + 节点拆分

注意到方法一、二需要使用哈希表记录每一个节点对应新节点的创建情况,而我们可以使用一个小技巧来省去哈希表的空间

  1. 首先将该链表中每一个节点拆分为两个相连的节点,例如对于链表 A → B → C A \rightarrow B \rightarrow C ABC ,我们可以将其拆分为 A → A ′ → B → B ′ → C → C ′ A \rightarrow A' \rightarrow B \rightarrow B' \rightarrow C \rightarrow C' AABBCC 。对于任意一个原节点 S S S ,其拷贝节点 S ′ S' S 即为其后继节点。
  2. 这样,我们可以直接找到「每一个拷贝节点 S ′ S' S 的随机指针」应当指向的节点,即为其「原节点 S S S 的随机指针指向的节点 T T T 」的后继节点 T ′ T' T 。需要注意原节点的随机指针可能为空,我们需要特别判断这种情况。
  3. 当完成了拷贝节点的随机指针的赋值,我们只需将这个链表按照原节点与拷贝节点的种类进行拆分即可,只需要遍历一次。同样需要注意最后一个拷贝节点的后继节点为空,我们需要特别判断这种情况。
class Solution {
public:
    Node* copyRandomList(Node* head) {
        if (head == nullptr) return nullptr;
        for (Node* node = head; node; node = node->next->next) {
            Node* nodeNew = new Node(node->val);
            nodeNew->next = node->next;
            node->next = nodeNew;
        }
        for (Node* node = head; node; node = node->next->next) {
            Node* nodeNew = node->next;
            nodeNew->random = node->random ? node->random->next : nullptr;
        }
        Node* headNew = head->next;
        for (Node* node = head; node; node = node->next) {
            Node* nodeNew = node->next;
            node->next = node->next->next;
            nodeNew->next = nodeNew->next ? nodeNew->next->next : nullptr;
        }
        return headNew;
    }
};

复杂度分析:

  • 时间复杂度: O ( n ) O(n) O(n) ,其中 n n n 是链表的长度。如果在计算拷贝节点的随机指针的同时计算其后继指针,只需遍历两次(好像需要修改原链表)。
  • 空间复杂度: O ( 1 ) O(1) O(1) 。注意返回值不计入空间复杂度。

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

四维纵横与用友达成战略合作,携手打造企业数据智能新基座

近日&#xff0c;北京四维纵横数据技术有限公司&#xff08;四维纵横 YMatrix&#xff09;与用友网络科技股份有限公司&#xff08;用友&#xff09;&#xff0c;宣布达成产品战略合作协议。双方将共同致力于为企业提供一站式数据智能解决方案&#xff0c;加速企业应用的全方位…

linux sed 删除空行、替换文本

假设文件&#xff1a;sed.txt first second third fourfive①删除four、five之间的空行 ②替换first为hsj is smart cat sed.txt | sed /^$/d | sed s/first/hsj is smart/g

【Unity-Cinemachine相机】相机跟随之Transposer属性

相机跟随和瞄准行为 Transposer&#xff1a;虚拟相机将在某个固定的偏移或距离上跟随目标移动 上面的偏移量就是Follow Offset Binding Mode决定Follow Offset是目标本地坐标系下的身后十米还是世界坐标系下的身后十米 Lock To Target On Assign&#xff1a;锁定自己和目标本地…

论文浅尝 | 训练语言模型遵循人类反馈的指令

笔记整理&#xff1a;吴亦珂&#xff0c;东南大学硕士&#xff0c;研究方向为大语言模型、知识图谱 链接&#xff1a;https://arxiv.org/abs/2203.02155 1. 动机 大型语言模型&#xff08;large language model, LLM&#xff09;可以根据提示完成各种自然语言处理任务。然而&am…

【LeetCode-中等题】208. 实现 Trie (前缀树)

文章目录 题目方法一&#xff1a;利用数组构建26叉树方法二&#xff1a;利用哈希表构建26叉树 题目 方法一&#xff1a;利用数组构建26叉树 插入图示&#xff1a; 全搜索和前缀搜索&#xff1a; 注意&#xff1a;全局匹配匹配完直接返回插入时的标志位 而前缀匹配时&#xff…

Vagrant命令

文章目录 1.介绍2.下载3. 配置3.1 配置环境变量3.2 在xshell中连接使用 4. 相关命令4.1 Box相关4.2 初始化环境4.4 虚拟机相关 1.介绍 Vagrant 是一个虚拟机管理工具 2.下载 https://www.vagrantup.com/ 3. 配置 3.1 配置环境变量 测试安装是否成功 3.2 在xshell中连接使…

MybatisPlus插件功能详细介绍 自动分页 通用分页实体

本课程全面讲解了Mybatis框架的使用&#xff0c;从快速入门到原理分析再到实战应用。每一个知识点都有案例进行演示学习&#xff0c;最终通过学习你将全面掌握&#xff0c;从而使Mybatis的开发更加的高效&#xff0c;系统学习 通过项目的开发大家应该能发现&#xff0c;单表的C…

同是卫星,华为Mate 60 pro的天通卫星和北斗卫星有何区别?

作为一个海钓爱好者&#xff0c;在看到华为Mate 60 Pro带有卫星通话功能那一刻&#xff0c;我就知道&#xff0c;我的钱包要-7000了——因为卫星通话功能&#xff0c;对于我们这类有着小众爱好的人群来说&#xff0c;实在太需要了。 对比北斗卫星短消息&#xff0c;Mate 60 Pr…

[git] 如何克隆仓库,进行项目撰写,并绑定自己的远程仓库

摘要&#xff1a;删除.git文件&#xff0c;才可重新绑定远程仓库。 具体步骤&#xff1a; 文件夹右键&#xff0c;进入”Git Bash Here“执行命令 1. 执行 ”git clone 仓库地址“&#xff0c;克隆仓库 2. 在生成的仓库中&#xff0c;删除 .git 文件 3. git init 初始化仓库…

3D渲染:面法线和顶点法线

现在我们回顾了影响对象外观的参数&#xff08;它们的亮度、颜色等&#xff09;&#xff0c;我们准备开始研究一些简单的着色技术。 推荐&#xff1a;用 NSDT编辑器 快速搭建可编程3D场景 1、法线 法线在着色中起着核心作用。 大家都知道&#xff0c;如果我们将物体朝向光源&a…

Unity ShaderGraph教程——进阶shader(水面、积雪,数字线框)

1.水面&#xff08;一&#xff09; 公式&#xff1a;场景深度 节点深度 — 屏幕空间位置的W向量 半透明物体与不透明物体的相交边缘 原理&#xff1a;场景深度 节点深度包含透明像素&#xff0c;屏幕空间w向量不包含透明像素。 注意&#xff1a;需要在UniversalRP-xxxQuali…

diskqueue怎么写入消息,怎么对外发送消息

nsq中diskqueue是nsq消息持久化的核心&#xff0c;内容较多&#xff0c;一共分为多篇 1. diskqueue是什么&#xff0c;为什么需要它&#xff0c;整体架构图&#xff0c;对外接口_YZF_Kevin的博客-CSDN博客 2. diskqueue的元数据文件&#xff0c;数据文件&#xff0c;启动入口…

成都睿趣科技:现在开一家抖音小店还来得及吗

随着社交媒体的迅猛发展&#xff0c;抖音已经成为了一个全球范围内广受欢迎的社交平台。在这个短视频应用上&#xff0c;人们分享着各种各样的内容&#xff0c;从搞笑段子到美食教程&#xff0c;再到时尚搭配和手工艺品制作。随着用户数量的不断增长&#xff0c;很多人都在思考…

Python第三方库 - matplotlib库

1 matplotlib了解 Matplotlib 可能是 Python 2D - 绘图领域使用最广泛的套件。它能让使用者很轻松地将数据图形化&#xff0c;并且提供多样化的输出格式。这里将会探索 matplotlib 的常见用法。 2 matplotlib学习 2.1 引用 plt 表示当前子图&#xff0c;若没有就创建一个子图 …

x64dbg的安装

一、安装地址&#xff1a; 地址 解压目录 点击x96dbf.exe 二、使用 1.反汇编窗口 这个位置显示的是需要分析的程序的反汇编代码。在第一个区域的最左侧例如“7712EAA3”这一列就是内存地址区域&#xff0c;接着“E8 07”就是汇编指令的opcode&#xff0c;“jmp xxxxxxxxx”这…

天津Java培训机构 Java的发展空间如何?

在当今互联网时代&#xff0c;计算机技术的发展日新月异&#xff0c;越来越多人看到IT行业的广泛前景&#xff0c;纷纷想要转行成为一名程序员&#xff0c;作为一名IT从业人员&#xff0c;学习一门编程语言是必不可少的&#xff0c;而在众多编程语言中&#xff0c;Java无疑是较…

存储数据恢复- raid5多块硬盘出现坏道的数据恢复案例

存储数据恢复环境&#xff1a; 某单位一台存储&#xff0c;1个机头4个扩展柜&#xff0c;有两组分别由27块和23块硬盘组建的RAID5阵列。其中由27块磁盘组建的那一组RAID5阵列崩溃&#xff0c;这组RAID5阵列存放是Oracle数据库文件。存储系统上层共划分了11个卷。 存储故障&…

【工具】Linux下常用录屏软件

&#x1f41a;作者简介&#xff1a;花神庙码农&#xff08;专注于Linux、WLAN、TCP/IP、Python等技术方向&#xff09;&#x1f433;博客主页&#xff1a;花神庙码农 &#xff0c;地址&#xff1a;https://blog.csdn.net/qxhgd&#x1f310;系列专栏&#xff1a;善假于物&#…

深圳企业宣传片怎么做

要拍摄企业宣传片&#xff0c;首先要搞清楚客户宣传片的目的和用途&#xff0c;然后根据自身情况拟定预算以及制作周期&#xff0c;再与甲方沟通具体需求&#xff0c;最后进入制作流程。整体制作流程可以分为以下步骤&#xff0c;由深圳企业宣传片制作公司老友记小编为您解答&a…

LeetCode--HOT100题(48)

目录 题目描述&#xff1a;437. 路径总和 III&#xff08;中等&#xff09;题目接口解题思路代码 PS: 题目描述&#xff1a;437. 路径总和 III&#xff08;中等&#xff09; 给定一个二叉树的根节点 root &#xff0c;和一个整数 targetSum &#xff0c;求该二叉树里节点值之和…