【LeetCode-中等】剑指 Offer 35. 复杂链表的复制(详解)

目录

题目

方法1:错误的方法(初尝试)

方法2:复制、拆开

方法3:哈希表

总结


题目

请实现 copyRandomList 函数,复制一个复杂链表。在复杂链表中,每个节点除了有一个 next 指针指向下一个节点,还有一个 random 指针指向链表中的任意节点或者 null

 

题目地址:剑指 Offer 35. 复杂链表的复制 - 力扣(LeetCode)

或同题:138. 复制带随机指针的链表 - 力扣(LeetCode) 

方法1:错误的方法(初尝试)

思路

1.先通过递归 创建新链表,每个节点的val 和 next 与旧的链表对应关系相同

2.再通过 原链表中每个节点的random的val 来找到新链表中每个节点的random的指向

这种方法是错误的方法(可以直接去看后面的方法),只是作者刚开始的尝试,错误的原因在于:每个节点的val不是唯一的,这样的话,你第2步用val来复制每个节点的random是不可以的,有些示例会过不去。

例如

输入:head = [[7,null],[13,0],[11,4],[10,2],[1,0]]

        那么第1步的结果是:newNode =  [[7,null],[13,null],[11,null],[10,null],[1,null]]

        然后 第2步就是对每个节点的 random进行指向。

代码

class Solution {public Node copyRandomList(Node head) {//复制节点的val、nextNode newNode = copy(head);Node p1 = head;Node p2 = newNode;//复制节点的 randomwhile (p1 != null) {if (p1.random != null) {int val = p1.random.val;p2.random = findNode(val,newNode);}p1 = p1.next;p2 = p2.next;}return newNode;}/*** 复制 Node 的 val 和 next*/Node copy(Node oldNode) {if (oldNode == null) return null;Node newNode = new Node(oldNode.val);newNode.next = copy(oldNode.next);return newNode;}/*** 找到某个节点:他属于head,并且val 是 val*/Node findNode(int val,Node head){if (head == null)return null;Node p = head;while (p!=null){if (p.val == val)return p;p = p.next;}return null;}
}

方法2:复制、拆开

思路来自

作者:王尼玛链接:https://leetcode.cn/problems/copy-list-with-random-pointer/solutions/295083/liang-chong-shi-xian-tu-jie-138-fu-zhi-dai-sui-ji-/来源:力扣(LeetCode)著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

 

第一步,根据遍历到的原节点创建对应的新节点,每个新创建的节点是在原节点后面,比如下图中原节点1不再指向原原节点2,而是指向新节点1 第二步是最关键的一步,用来设置新链表的随机指针

 

上图中,我们可以观察到这么一个规律

原节点1的随机指针指向原节点3,新节点1的随机指针指向的是原节点3的next
原节点3的随机指针指向原节点2,新节点3的随机指针指向的是原节点2的next
也就是,原节点i的随机指针(如果有的话),指向的是原节点j
那么新节点i的随机指针,指向的是原节点j的next

第三步就简单了,只要将两个链表分离开,再返回新链表就可以了

 

代码


class Solution {public Node copyRandomList(Node head) {if(head==null) {return null;}Node p = head;//第一步,在每个原节点后面创建一个新节点//1->1'->2->2'->3->3'while(p!=null) {Node newNode = new Node(p.val);newNode.next = p.next;p.next = newNode;p = newNode.next;}p = head;//第二步,设置新节点的随机节点while(p!=null) {if(p.random!=null) {p.next.random = p.random.next;}p = p.next.next;}//第三步,将两个链表分离(注意这里是分离,不能修改原来的链表)Node res = new Node(-1);Node oldNode = head;Node newNode = res;while (oldNode!=null){newNode.next = oldNode.next;newNode = newNode.next;oldNode.next = newNode.next;oldNode = oldNode.next;}return res.next;}
}

方法3:哈希表

思路

思路来自

作者:王尼玛链接:https://leetcode.cn/problems/copy-list-with-random-pointer/solutions/295083/liang-chong-shi-xian-tu-jie-138-fu-zhi-dai-sui-ji-/来源:力扣(LeetCode)著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

我们用哈希表来解决这个问题
首先创建一个哈希表,再遍历原链表,遍历的同时再不断创建新节点
我们将原节点作为key,新节点作为value放入哈希表中

 第二步我们再遍历原链表,这次我们要将新链表的next和random指针给设置上

 

从上图中我们可以发现,原节点和新节点是一一对应的关系,所以

  • map.get(原节点),得到的就是对应的新节点
  • map.get(原节点.next),得到的就是对应的新节点.next
  • map.get(原节点.random),得到的就是对应的新节点.random

所以,我们只需要再次遍历原链表,然后设置:
新节点.next -> map.get(原节点.next)
新节点.random -> map.get(原节点.random)
这样新链表的next和random都被串联起来了
最后,我们然后map.get(head),也就是对应的新链表的头节点,就可以解决此问题了。

代码

class Solution {public Node copyRandomList(Node head) {if(head==null) {return null;}//创建一个哈希表,key是原节点,value是新节点Map<Node,Node> map = new HashMap<Node,Node>();Node p = head;//将原节点和新节点放入哈希表中while(p!=null) {Node newNode = new Node(p.val);map.put(p,newNode);p = p.next;}p = head;//遍历原链表,设置新节点的next和randomwhile(p!=null) {Node newNode = map.get(p);//p是原节点,map.get(p)是对应的新节点,p.next是原节点的下一个//map.get(p.next)是原节点下一个对应的新节点if(p.next!=null) {newNode.next = map.get(p.next);}//p.random是原节点随机指向//map.get(p.random)是原节点随机指向  对应的新节点 if(p.random!=null) {newNode.random = map.get(p.random);}p = p.next;}//返回头结点,即原节点对应的value(新节点)return map.get(head);}
}

总结

最好的方法我觉得还是方法3,这个方法不仅思路简单,代码也简单。方法2虽然思路简单,但是写代码不好写,所以要多去想哈希表,原来哈希表的key 和 value 可以分别存放两个链表,所以以后看到复杂链表的复制,要去想用哈希表来复制。

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

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

相关文章

数字滚动变化-指令形式

话不多说&#xff0c;直接上代码 <template><divv-data-scroll"{target: 100speed: 1000}">100</div> </template><script setup lang"ts"> import { DirectiveBinding } from vue;function dataScroll(el: HTMLElement, …

2023电赛E题视觉部分

该部分主要要完成正方形区域的识别&#xff0c;并返回对应的坐标&#xff0c;但是由于距离1m&#xff0c;过远。因此需要引入图像增强&#xff0c;下面代码完成基本流程测试&#xff0c;仅供参考&#xff1a; import sensor import image import time # 初始化摄像头 senso…

WEB 文件包含 /伪协议

首先谈谈什么是文件包含 WEB入门——文件包含漏洞与PHP伪协议_文件包含php伪协议_HasntStartIsOver的博客-CSDN博客 文件包含 程序员在编写的时候 可能写了自己的 函数 如果想多次调用 那么就需要 重新写在源代码中 太过于麻烦了只需要写入 funcation.php然后在需要引用的地…

Vue3使用Mitt中央事件总线实现组件之间通讯(发布订阅库)

前言 现在的项目慢慢从 Vue2 升级到 Vue3 了&#xff0c;之前 Vue2 自带的中央事件总线是 EventBus&#xff0c;在 Vue3 中已经被移除了&#xff0c;官方推荐使用 Mitt 发布订阅库。在此简单记录一下 Mitt 的使用方式。 一、导入依赖 npm i mitt -D 二、全局引入 &#xf…

【iOS】—— 循环引用问题总结

循环引用 文章目录 循环引用1.自循环引用2.相互循环引用3.多循环引用 常见的循环引用问题1.delegate解决方法&#xff1a; 2.block解决方法&#xff1a;1.强弱共舞2.把当前类作为block的参数3.用__block修饰变量&#xff0c;在block内部置nil 3.NSTimer解决方案&#xff1a;1.使…

linux ftp

使用ftp连接本机进行文件传输 1、下载vsftpd服务器程序 apt install vsftpd 2、使用tcp抓包 tcpdump -nt -i lo port 20 在FTP连接到本地主机&#xff08;127.0.0.1&#xff09;时&#xff0c;数据可能通过本地回环接口&#xff08;loopback interface&#xff09;传输&…

vue学习之插值表达式{{}}与显示数据(v-text和v-html)

1. 记得导入 <!-- 在线导入 --> <!-- 开发环境版本&#xff0c;包含了用帮助的命令行警告 --> <script src"https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script> <!-- 生产环境版本&#xff0c;优化了尺寸和速度 --> <scri…

C语言中的字符串操作函数:深入探索string.h头文件

字符串操作函数&#xff1a;string.h 在C语言中&#xff0c;string.h是一个标准头文件&#xff0c;提供了许多用于操作字符串的函数。这些函数对于处理字符串、拷贝、连接和搜索等操作非常有用。本篇博客将详细介绍string.h头文件中的一些主要函数&#xff0c;并且使用C语言进…

【JS】浏览器不同窗口、标签页或 iframe之间的通讯 - 技术的尽头是魔术

效果 左上↖地址: http://127.0.0.1:5500/index.html 左下↙地址: http://127.0.0.1:5500/index.html?hidden 右上↗地址: http://127.0.0.1:5500/index.html?hidden 右下↘地址: http://127.0.0.1:5500/index.html?hidden index.html <!DOCTYPE html> <html>…

使用自适应去噪在线顺序极限学习机预测飞机发动机剩余使用寿命(Matlab代码实现)

&#x1f4a5;&#x1f4a5;&#x1f49e;&#x1f49e;欢迎来到本博客❤️❤️&#x1f4a5;&#x1f4a5; &#x1f3c6;博主优势&#xff1a;&#x1f31e;&#x1f31e;&#x1f31e;博客内容尽量做到思维缜密&#xff0c;逻辑清晰&#xff0c;为了方便读者。 ⛳️座右铭&a…

搜索与图论(三)

一、最小生成树 1.1Prim算法 朴素版Prim 一般用于稠密图 算法流程: 集合表示当前已经在连通块的点 1.初始化距离&#xff0c;把所有距离都初始化为正无穷 2.n次迭代,找到集合外距离最小的点 ->t 3.用t来更新其它点到集合的距离 #include<iostream> #include&…

小红书博主排名丨狂揽近百万粉丝,女性议题成“爆款制造机”?

从上野千鹤子和北大女生的对谈&#xff0c;到电影《消失的她》&#xff0c;再到引爆“粉色狂潮”的电影《芭比》&#xff0c;近年来&#xff0c;女性话题、两性情感话题成为社会热门议题。“踩过恋爱所有坑&#xff0c;想给姑娘撑把伞”&#xff0c;近期&#xff0c;小红书博主…

Qt tabwidget中插入widget

一、简单介绍 QT->tabWidget&#xff1a;标签页面。 在ui中通过工具栏自定义拉取控件&#xff0c;其中tabwidget可以可以创建多个标签页面&#xff0c;默认生成两个tab_widget(tab_1/tab_2)。并且可以在ui中右键自由添加控制删除等标签页&#xff0c;切换标签页就是切换widg…

Greenplum集群部署

一,安装说明 1.1环境说明 *名称**版本*操作系统CentOS 7.6 64bitgreenplumgreenplum-db-6.10.1-rhel7-x86_64.rpm1.2集群介绍 IPhostname集群节点10.240.3.244gpmastermaster10.240.3.245gpsegment1segment10.240.3.246gpsegment2segment二,安装环境准备 2.1 修改各节点名称…

Uniapp_app端使用重力感应实现横屏竖屏自动切换

1、进入页面默认是竖屏当手机横着的时候页面也跟着横着 进入页面开启定时器调用相关api去触发横屏竖屏&#xff0c;主要核心代码都在onShow()里面和onHide()里 <template> <view class"monitor"><u-no-network></u-no-network><web-view …

基于组合双向拍卖的共享储能机制研究(Matlab代码实现)

目录 &#x1f4a5;1 概述 &#x1f4da;2 运行结果 2.1 算例数据 2.2 买家中标 2.3 卖家中标 &#x1f389;3 文献来源 &#x1f308;4 Matlab代码实现 &#x1f4a5;1 概述 文献来源&#xff1a; 摘要&#xff1a;为满足共享储能中储能用户的互补性和替代性需求、解决常规单…

一文说清楚支付架构

作者&#xff1a;陈斌 支付的技术架构是为了保障能够顺利处理支付请求而设计的结构体系。从系统的角度看&#xff0c;它包括了计算机系统的软件、硬件、网络和数据等。从参与的主体角度来看&#xff0c;它涉及交易的付款方、收款方、支付机构、银行、卡组织和金融监管机构等。要…

无涯教程-Lua - while语句函数

只要给定条件为真&#xff0c;Lua编程语言中的 while 循环语句就会重复执行目标语句。 while loop - 语法 Lua编程语言中 while 循环的语法如下- while(condition) dostatement(s) end while loop - 流程图 在这里&#xff0c;需要注意的关键是 while 循环可能根本不执行。…

SOC FPGA之流水灯设计

一、DS-5简介 Altera Soc EDS开发套件的核心是Altera版ARM Development Studio 5(DS-5)工具包&#xff0c;为SoC器件提供了完整的嵌入式开发环境、FPGA自适应调试和对Altera工具的兼容。 1.1 DS-5 eclipse破解 首先下载破解器 然后进入cmd运行&#xff0c;进入到破解器所在文…

XCode升级后QT无法编译的问题

原因是SDK的版本变了&#xff0c;Qt配置的版本要修改。 解决办法如下&#xff1a; 1.找到 /Users/*/Qt/5.15.2/clang_64/mkspecsqdevice.pri 这个文件打开编辑&#xff0c; 在文件末尾追加一句 !host_build:QMAKE_MAC_SDKmacosx13.1 至于这个版本号13.1是怎么来的呢&#xff1…