当前位置: 首页 > news >正文

力扣LeetCode138. 复制带随机指针的链表 两种解法(C语言实现)

目录

题目链接

题目分析

 题目定位:

解题思路

解题思路1(粗暴但是复杂度高)

 解题思路2(巧妙并且复杂度低)


题目链接

138. 复制带随机指针的链表icon-default.png?t=N7T8https://leetcode-cn.com/problems/copy-list-with-random-pointer/

题目分析

 题目定位:

本题属于链表中较为综合的题目,考验做题者的思想以及用代码实现的能力,能够真正理解并做出此题需要对链表有相对熟练的掌握度。


话不多说,先来分析题目:

题目是想让我们构造一个链表的拷贝,也就是开辟一块一模一样的链表,但是本题中的链表,又和普通的单链表不一样,如图:

我们发现,链表中每一个节点中的结构体成员除了valnext之外,还有一个random,而random是指向链表中的任意一个节点,甚至可以是NULL。

因此要拷贝这个链表的难度就在于,如何使拷贝的链表中每一个节点的random指向其对应的节点呢?

解题思路

解题思路1(粗暴但是复杂度高)

第1步.先忽略原链表的random指针,对其进行复制,如图:

(1)定义两个struct Node*类型的指针copyhead和copytail来储存复制链表的头和尾,并初始化为NULL;并且定义一个struct Node*类型的指针cur指向head


(2)若原链表不为空,则此时cur指向第一个节点。用malloc开辟一块空间作为复制的第一个节点,并定义一个copy指针来保存,并将copy->val改为cur->val,并让copyhead和copytail指向copy节点


(3)让cur移向下一个节点。用malloc开辟一块空间作为复制的第二个节点,并让copy指向它,并将copy->val改为cur->val,先让copytail->next指向copy来连接两个节点,再让copytail指向copy节点(更新尾节点)。

像这样一直循环下去直到cur指向NULL,循环结束,最后再让copytail->next = NULL,此时便完成了第一步的复制操作

此时我们便可以根据图解来写代码

struct Node* copyRandomList(struct Node* head) {//1.忽略random复制链表struct Node* cur = head;struct Node* copyhead = NULL;struct Node* copytail = NULL;while(cur){struct Node* copy = (struct Node*)malloc(sizeof(struct Node));copy->val = cur->val;if(copyhead == NULL){copyhead = copy;copytail = copy;}else{//先连接copytail->next = copy;//再指向copycopytail = copy;}cur = cur->next;}copytail->next = NULL;
}

第2步.处理拷贝链表的random指针

(1)查找原链表中的random指向的节点到底是第几个节点,并定义一个变量pos来记录下来

①如果原链表random指向NULL,则不需要找pos

②如果原链表的random指向链表中的节点,每次循环开始可以初始化一个新的指针newcur=head,再对newcur进行迭代,每当newcur向后走一次,pos加一,直到newcur == cur->random的时候,循环结束,举一个例子:


 (2)用一个循环来使拷贝链表中random指向其对应的节点

①如果原链表的random指向NULL,那么便很简单:拷贝的random直接置为NULL;

②如果原链表的random指向链表中的节点,每次循环开始可以初始化一个新的指针newcopy=copyhead,那么拷贝的random则可以通过一个循环来找到pos所对应的第几个节点,接着上面的例子:

根据图解我们可以写出代码:

struct Node* copyRandomList(struct Node* head) {//1.忽略random复制链表struct Node* cur = head;struct Node* copyhead = NULL;struct Node* copytail = NULL;while (cur){struct Node* copy = (struct Node*)malloc(sizeof(struct Node));copy->val = cur->val;if (copyhead == NULL){copyhead = copy;copytail = copy;}else{//先连接copytail->next = copy;//再指向copycopytail = copy;}cur = cur->next;copytail->next = NULL;}//2.处理拷贝链表的random指针cur = head;struct Node* copy = copyhead;while (cur){int pos = 0;if (cur->random == NULL){copy->random = NULL;}//找到cur的random指向对应的第几个节点else{//让newcur和newcopy重新指向第一个节点struct Node* newcur = head;struct Node* newcopy = copyhead;while (newcur != cur->random){newcur = newcur->next;pos++;}//使copy的random指向对应的第pos个节点while (pos--){newcopy = newcopy->next;}copy->random = newcopy;}//cur和copy向后走cur = cur->next;copy = copy->next;}return copyhead;
}

 题解一总结:优点是这种思路比较容易想到并且理解;缺点是由于找到原链表找到random指向的节点的pos的过程,每个节点的复杂度为O(n),又有n个节点,因此这种解法的时间复杂度为O(n²),并不是一个优秀的解法,接下来将为大家讲另一种优秀的解法。


 解题思路2(巧妙并且复杂度低)

第1步 把拷贝节点连接再原节点的后面

用这种方式处理是为了方便找到拷贝链表的random应该指向第几个节点,random指向的这个节点的下一个节点就是拷贝链表要找的random节点

根据图解我们可以写出代码:

//1.链接链表struct Node* cur = head;while(cur){//提前储存原链表下一个节点的地址struct Node* next = cur->next;//为原链表的拷贝开辟空间struct Node* copy = (struct Node*)malloc(sizeof(struct Node));copy->val = cur->val;//链接原链表和拷贝节点cur->next = copy;copy->next = next;//迭代往下走cur = next;}

 第2步 拷贝原链表的random节点

 (1)若原链表的节点的random指向NULL,拷贝节点的random也指向NULL


(2)若原链表的节点random指向原链表的其中一个节点,那么random指向的这个节点的下一个节点就是拷贝链表要找的random,以原链表二个节点为例:

(3)实现后题解如下

根据图解我们可以写出代码:

//2.拷贝原链表的random节点cur = head;while(cur){struct Node* copy = cur->next;if(cur->random == NULL){copy->random = NULL;}else{copy->random = cur->random->next;}cur = copy->next;}

第3步 将拷贝节点与原链表分开

(1)用cur和copy指针来分别使原链表和拷贝链表的节点向后迭代,由于copy随着cur向后迭代,一直跟在cur的后面

因此循环外初始化cur = head,循环内初始化copy = cur->next;

 (2)为原链表定义一个next来连接链表;为拷贝链表定义一个copyhead和copytail来返回和连接链表,当原链表第一个节点不为空,才能保证copy的第一个节点不为空,因此copyhead和copytail先置空

循环外初始化copytail = copyhead = NULL,循环内初始化next = copy->next, 

(3)当cur指向第一个节点时,进入循环,此时copyhead和copytail为空,使其指向拷贝链表的第一个节点,再使cur->next = next,将cur和next连接起来,并使cur=next往后走,

 若cur不为空,再之后就是让copy=cur->next,next = copy->next

  捋清楚结构就可以写出循环体框架:

    cur = head;struct Node* copyhead = NULL;struct Node* copytail = NULL;while(cur){struct Node* copy = cur->next;struct Node* next = copy->next;if(copyhead == NULL){copyhead = copytail = copy;}cur->next = next;cur = next;}

(4)当cur指向第二个节点时,copyhead和copytail此时不为空,copy也指向拷贝的第二个节点,因此用copytail->next = copy连接两节点,再使copytail = copy使其向后迭代

捋清关系我们便可以补全循环体

//3.将拷贝节点与原链表分开cur = head;struct Node* copyhead = NULL;struct Node* copytail = NULL;while(cur){struct Node* copy = cur->next;struct Node* next = copy->next;if(copyhead == NULL){copyhead = copytail = copy;}else{copytail->next = copy;copytail = copy;}cur->next = next;cur = next;}

完整代码: 

struct Node* copyRandomList(struct Node* head) {//1.链接链表struct Node* cur = head;while(cur){//提前储存原链表下一个节点的地址struct Node* next = cur->next;//为原链表的拷贝开辟空间struct Node* copy = (struct Node*)malloc(sizeof(struct Node));copy->val = cur->val;//链接原链表和拷贝节点cur->next = copy;copy->next = next;//迭代往下走cur = next;}//2.拷贝原链表的random节点cur = head;while(cur){struct Node* copy = cur->next;if(cur->random == NULL){copy->random = NULL;}else{copy->random = cur->random->next;}cur = copy->next;}//3.将拷贝节点与原链表分开cur = head;struct Node* copyhead = NULL;struct Node* copytail = NULL;while(cur){struct Node* copy = cur->next;struct Node* next = copy->next;if(copyhead == NULL){copyhead = copytail = copy;}else{copytail->next = copy;copytail = copy;}cur->next = next;cur = next;}return copyhead;
}

 题解二总结:由于第二种解题思路用巧妙的连接方式,使拷贝链表的random不需要通过对原链表的每一个节点遍历也能找到,大大降低了时间复杂度,这种解法的时间复杂度为O(n)

相关文章:

力扣LeetCode138. 复制带随机指针的链表 两种解法(C语言实现)

目录 题目链接 题目分析 题目定位: 解题思路 解题思路1(粗暴但是复杂度高) 解题思路2(巧妙并且复杂度低) 题目链接 138. 复制带随机指针的链表https://leetcode-cn.com/problems/copy-list-with-random-pointer/ …...

强大的压缩和解压缩工具 Keka for Mac

Keka for Mac是一款功能强大的压缩和解压缩工具,专为Mac用户设计。它支持多种压缩格式,包括7z、Zip、Tar、Gzip和Bzip2等,无论是发送电子邮件、备份文件还是节省磁盘空间,Keka都能轻松满足用户需求。 这款软件的操作简单直观&…...

论文速读:Do Generated Data Always Help Contrastive Learning?

在对比学习领域,最近很多研究利用高质量生成模型来提升对比学习 给定一个未标记的数据集,在其上训练一个生成模型来生成大量的合成样本,然后在真实数据和生成数据的组合上执行对比学习这种使用生成数据的最简单方式被称为“数据膨胀”这与数据…...

华为欧拉系统(openEuler-22.03)安装深信服EasyConnect软件(图文详解)

欧拉镜像下载安装 iso镜像官网下载地址 选择最小化安装,标准模式 换华为镜像源 更换华为镜像站,加速下载: sed -i "s#http://repo.openeuler.org#https://mirrors.huaweicloud.com/openeuler#g" /etc/yum.repos.d/openEuler.r…...

git commit --amend用法

一、git commit --amend 修改提交信息:您可以使用 git commit --amend 命令来修改最新提交的提交信息。执行该命令后,Git 将会打开文本编辑器(通常是的默认文本编辑器),以便编辑提交信息。完成编辑后保存并关闭编辑器…...

分布式系统:缓存与数据库一致性问题

前言 缓存设计是应用系统设计中重要的一环,是通过空间换取时间的一种策略,达到高性能访问数据的目的;但是缓存的数据并不是时刻存在内存中,当数据发生变化时,如何与数据库中的数据保持一致,以满足业务系统…...

JavaEE企业开发新技术5

目录 2.18 综合应用-1 2.19 综合应用-2 2.20 综合应用-3 2.21 综合应用-4 2.22 综合应用-5 Synchronized : 2.18 综合应用-1 反射的高级应用 DAO开发中,实体类对应DAO的实现类中有很多方法的代码具有高度相似性,为了提供代码的复用性,降低…...

mysql dump导出导入数据

前言 mysqldump是MySQL数据库中一个非常有用的命令行工具,用于备份和还原数据库。它可以将整个数据库或者特定的表导出为一个SQL文件,以便在需要时进行恢复或迁移。 使用mysqldump可以执行以下操作: 备份数据库:可以使用mysqld…...

刷题记录3

# 10 字符个数统计 描述 编写一个函数,计算字符串中含有的不同字符的个数。字符在 ASCII 码范围内( 0~127 ,包括 0 和 127 ),换行表示结束符,不算在字符里。不在范围内的不作统计。多个相同的字符只计算一次 例如,对…...

Decorator 装饰

意图 动态的给一个对象添加一些额外的职责。就增加功能而言,Decorator模式比生成子类更加灵活 结构 其中: Component定义一个对象接口,可以给这些对象动态的添加职责。ConcreteComponent定义一个对象,可以给这个对象添加一些职…...

SpringMVC:搭建第一个web项目并配置视图解析器

👉需求:用spring mvc框架搭建web项目,通过配置视图解析器达到jsp页面不得直接访问,实现基本的输出“hello world”功能。👩‍💻👩‍💻👩‍💻 1 创建web项目 1…...

一文了解HTTPS的加密原理

HTTPS是一种安全的网络通信协议,用于在互联网上提供端到端的加密通信,确保数据在客户端(如Web浏览器)与服务器之间传输时的机密性、完整性和身份验证。HTTPS的加密原理主要基于SSL/TLS协议,以下详细阐述其工作过程&…...

Ubuntu系统空间整理

查看文件大小命令 查看文件以及文件夹大小 ls -hl #查看文件大小,-h 表示Human-Readable ll -h #查看文件夹的大小 #du命令查看文件或文件夹的磁盘使用空间,–max-depth 用于指定深入目录的层数。#查看当前目录已经使用总大小及当前目录下一级文件或…...

PHP Storm 2024.1使用

本文讲的是phpstorm 2024.1最新版本激活使用教程,本教程适用于windows操作系统。 1.先去idea官网下载phpstorm包,我这里以2023.2最新版本为例 官网地址:https://www.jetbrains.com/zh-cn/phpstorm/ 2.下载下来后安装,点下一步 …...

王东岳-知鱼之乐【边读边记】1

2024-04-15 21:00 终于打算开始读这本书了,作者王东岳,第一次听到这个名字,是因为传说王东岳是李善友的师父,我是先从混沌学院知道的李善友,因为李善友还是有东西的,所以我对王东岳起步也是非常尊敬的。所以…...

迁移docker部署的GitLab

目录 1. 背景2. 参考3. 环境4. 过程4.1 查看原docker启动命令4.2 打包挂载目录传至新宿主机并创建对应目录4.3 保存镜像并传至新宿主机下4.4 新宿主机启动GitLab容器 5 故障5.1 容器不断重启5.2 权限拒绝5.3 容器内错误日志 6 重启容器服务正常7 总结 1. 背景 最近接到一个任务…...

今年消费新潮流:零元购商业模式

今天给大家推荐一种极具创新的电子商务模式:零元购商业模式 这个模式支持消费者以零成本或极低成本购买商品。这种模式主要通过返现、积分、优惠券等方式来减少支付金额,使消费者实现“零成本”购物的目标。 人民网在去年发表了一篇文章。 总结了一下&a…...

Go导入私有仓库

使用go.mod依赖第三方库时,有以下要求: 代码仓库托管于VCS(版本控制系统);代码仓库是公开的;仓库地址使用域名访问;仓库域名支持HTTPS访问。 对于自己或者公司内部搭建的私有git,这些条件是比较难同时满足…...

GIS GeoJSON数据获取

1、工具地址 DataV.GeoAtlas地理小工具系列 2、界面预览...

书生·浦语大模型实战营 | 第3次学习笔记

前言 书生浦语大模型应用实战营 第二期正在开营,欢迎大家来学习。(参与链接:https://mp.weixin.qq.com/s/YYSr3re6IduLJCAh-jgZqg 第三堂课的视频链接:https://www.bilibili.com/video/BV1QA4m1F7t4/ 本次笔记是学习完第三堂课…...

论文解读:交大港大上海AI Lab开源论文 | 宇树机器人多姿态起立控制强化学习框架(二)

HoST框架核心实现方法详解 - 论文深度解读(第二部分) 《Learning Humanoid Standing-up Control across Diverse Postures》 系列文章: 论文深度解读 + 算法与代码分析(二) 作者机构: 上海AI Lab, 上海交通大学, 香港大学, 浙江大学, 香港中文大学 论文主题: 人形机器人…...

Oracle查询表空间大小

1 查询数据库中所有的表空间以及表空间所占空间的大小 SELECTtablespace_name,sum( bytes ) / 1024 / 1024 FROMdba_data_files GROUP BYtablespace_name; 2 Oracle查询表空间大小及每个表所占空间的大小 SELECTtablespace_name,file_id,file_name,round( bytes / ( 1024 …...

YSYX学习记录(八)

C语言&#xff0c;练习0&#xff1a; 先创建一个文件夹&#xff0c;我用的是物理机&#xff1a; 安装build-essential 练习1&#xff1a; 我注释掉了 #include <stdio.h> 出现下面错误 在你的文本编辑器中打开ex1文件&#xff0c;随机修改或删除一部分&#xff0c;之后…...

什么是库存周转?如何用进销存系统提高库存周转率?

你可能听说过这样一句话&#xff1a; “利润不是赚出来的&#xff0c;是管出来的。” 尤其是在制造业、批发零售、电商这类“货堆成山”的行业&#xff0c;很多企业看着销售不错&#xff0c;账上却没钱、利润也不见了&#xff0c;一翻库存才发现&#xff1a; 一堆卖不动的旧货…...

鱼香ros docker配置镜像报错:https://registry-1.docker.io/v2/

使用鱼香ros一件安装docker时的https://registry-1.docker.io/v2/问题 一键安装指令 wget http://fishros.com/install -O fishros && . fishros出现问题&#xff1a;docker pull 失败 网络不同&#xff0c;需要使用镜像源 按照如下步骤操作 sudo vi /etc/docker/dae…...

Unit 1 深度强化学习简介

Deep RL Course ——Unit 1 Introduction 从理论和实践层面深入学习深度强化学习。学会使用知名的深度强化学习库&#xff0c;例如 Stable Baselines3、RL Baselines3 Zoo、Sample Factory 和 CleanRL。在独特的环境中训练智能体&#xff0c;比如 SnowballFight、Huggy the Do…...

算法岗面试经验分享-大模型篇

文章目录 A 基础语言模型A.1 TransformerA.2 Bert B 大语言模型结构B.1 GPTB.2 LLamaB.3 ChatGLMB.4 Qwen C 大语言模型微调C.1 Fine-tuningC.2 Adapter-tuningC.3 Prefix-tuningC.4 P-tuningC.5 LoRA A 基础语言模型 A.1 Transformer &#xff08;1&#xff09;资源 论文&a…...

[免费]微信小程序问卷调查系统(SpringBoot后端+Vue管理端)【论文+源码+SQL脚本】

大家好&#xff0c;我是java1234_小锋老师&#xff0c;看到一个不错的微信小程序问卷调查系统(SpringBoot后端Vue管理端)【论文源码SQL脚本】&#xff0c;分享下哈。 项目视频演示 【免费】微信小程序问卷调查系统(SpringBoot后端Vue管理端) Java毕业设计_哔哩哔哩_bilibili 项…...

【C++进阶篇】智能指针

C内存管理终极指南&#xff1a;智能指针从入门到源码剖析 一. 智能指针1.1 auto_ptr1.2 unique_ptr1.3 shared_ptr1.4 make_shared 二. 原理三. shared_ptr循环引用问题三. 线程安全问题四. 内存泄漏4.1 什么是内存泄漏4.2 危害4.3 避免内存泄漏 五. 最后 一. 智能指针 智能指…...

Webpack性能优化:构建速度与体积优化策略

一、构建速度优化 1、​​升级Webpack和Node.js​​ ​​优化效果​​&#xff1a;Webpack 4比Webpack 3构建时间降低60%-98%。​​原因​​&#xff1a; V8引擎优化&#xff08;for of替代forEach、Map/Set替代Object&#xff09;。默认使用更快的md4哈希算法。AST直接从Loa…...