代码随想录算法训练营第三天 | 链表理论基础 203.移除链表元素 707.设计链表 206.反转链表

链表理论基础

  • 链表是一种通过指针串连在一起的线性结构,每一个节点由两部分组成,一个是数据域,一个是指针域(存放指向下一个节点的指针)。最后一个节点的指针指向 null。
  • 链表的存储方式:数组在内存中是连续存储的,但链表不是连续存储的,各个节点分布在内存的不同地址空间中,用指针串联在一起。
  • 链表的定义:
// 单链表
struct ListNode{
	int val;  // 节点上存储的值
	ListNode *next;  // 指向下一个节点的指针
	ListNode(int x) : val(x), next(NULL) {}  // 链表节点的构造函数
};

C++ 也默认生成了一个构造函数,但这个构造函数不会初始化任何成员变量
我们自己的构造函数初始化链表节点:ListNode* head = new ListNode(5);
使用默认的构造函数初始化:ListNode* head = new ListNode(); head->val = 5;

  • 删除或增加节点:这个操作与其他节点是无关的,时间复杂度为 O(1)。但是在指定位置操作,还需要有查询操作。另外注意删除节点时只是改变指针指向,删除的节点仍在内存中,最好去主动释放。
  • 查找:链表只能从一端开始遍历,因此时间复杂度为 O(n)。
  • 性能分析:数组固定长度,连续存储,在删除或插入元素时,需要逐个移动元素,时间复杂度 O(n),但查找方便,时间复杂度 O(1)。链表长度并不固定,不连续存储,增删元素时间复杂度为 O(1),但查找 O(n)。
    因此数组适用于数据量固定,查找频繁,较少增删;链表适用于数据量不固定,增删频繁,较少查找的情景。

移除链表元素

Alt
之前我们做过一道移除链表元素的题,其难点在于数组连续存储,所以移除元素之后还需要移动其他元素保证连续。但是链表不需要保证连续存储,移除操作与其他元素无关的,其实我们直接遍历整条链表就可以了。
处理过程需要注意的问题:头节点与其他节点不同的处理办法。其他节点都是改变前一个节点的指针指向,但删除头节点的话需要不断向后更新头节点。可以添加一个虚拟节点 dummy 使得头节点的处理一般化

class Solution{
public:
	ListNode* removeElements(ListNode* head, int val){
		ListNode* dummy = new ListNode();
		dummy->next = head;
		ListNode* cur = dummy;
		while(cur->next != NULL){  // 用cur->next进行判断,注意删除节点释放内存的操作
			if(cur->next->val == val){
				ListNode* tmp = cur->next;
				cur->next = cur->next->next;
				delete tmp;
			}
			else  cur = cur->next;
		}
		head = dummy->next;
		delete dummy;
		return head;
	}
};

设计链表

Alt
注意C++语法。public 和 private 的前后顺序。维护一个虚拟节点和节点数目会使其他操作更加方便。

class MyLinkedList{
public:
	struct ListNode{
		int val;
		ListNode* next;
		ListNode(int val) : val(val), next(NULL) {}
	};
	MyLinkedList(){
		_dummyHead = new ListNode(0);
		_size = 0;
	}

	int get(int index){
		if(index < 0 || index > _size - 1)  return -1;
		ListNode* cur = _dummyHead->next;
		while(index--){
			cur = cur->next;
		}
		return cur->val;
	}
	void addAtHead(int val){
		ListNode* node = new ListNode(val);
		ListNode* cur = _dummyHead;
		node->next = _dummyHead->next;
		_dummyHead->next = node;
		_size++;
	}
	void addAtTail(int val){
		ListNode* node = new ListNode(val);
		ListNode* cur = _dummyHead;
		while(cur->next){
			cur = cur->next;
		}
		cur->next = node;
		_size++;
	}
	void addAtIndex(int index, int val){
		if(index < 0 || index > _size)  return;
		ListNode* node = new ListNode(val);
		ListNode* cur = _dummyHead;
		while(index--){
			cur = cur->next;
		}
		node->next = cur->next;
		cur->next = node;
		_size++;
	}
	void deleteAtIndex(int index){
		if(index < 0 || index > _size - 1)  return;
		ListNode* cur = _dummyHead;
		while(index--){
			cur = cur->next;
		}
		cur->next = cur->next->next;
		_size--;
	}
private:
	int _size;  // 记录链表中的节点数目
	ListNode* _dummyHead;  // 设计一个虚拟节点,解决头节点的问题
};

反转链表

Alt
链表只能头节点开始遍历,为了避免新建链表,可以选择使用双指针法。一个指针指向前一个节点,一个指针指向当前节点。注意:在遍历过程中会改变 next 指针的指向,所以要使用中间变量来记录下一个节点,再改变当前节点的 next 指针指向。

class Solution{
public:
	ListNode* reverseList(ListNode* head){
		if(!head)  return head;
		ListNode* cur = head;
		ListNode* pre = NULL;
		while(cur){
			ListNode* tmp = cur->next;  // 记录当前节点的下一个
			cur->next = pre;  // 翻转当前节点的指向
			pre = cur;  // 向后遍历
			cur = tmp;
		}
		return pre;
	}
};

递归法,上面的迭代法可以改写成递归。

class Solution{
public:
	ListNode* reverse(ListNode* pre, ListNode* cur){
		if(!cur)  return pre;
		ListNode* tmp = cur->next;
		cur->next = pre;
		return reverse(cur, tmp);
	}
	ListNode* reverseList(ListNode* head){
		return reverse(NULL, head);
	}
};

还有另一种比较难以想到的方法,是从后往前翻转。

class Solution{
public:
	ListNode* reverseList(ListNode* head){
		if(head == NULL)  return head;
		if(head->next == NULL)  return head;
		ListNode* last = reverseList(head->next);  // 把原链表末尾的节点返回(翻转后的头节点)
		head->next->next = head;  // 将head节点移到队列尾部,注意这一步没有改变原链表中指向head的指针,使得每层递归都能将当前head移动到队尾
		head->next = NULL;  // head移到队尾,指向空节点
		return last;
	}
};

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

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

相关文章

深度强化学习Task2:策略梯度算法

本篇博客是本人参加Datawhale组队学习第二次任务的笔记 【教程地址】 文章目录 基于价值算法和基于策略算法的比较策略梯度算法策略梯度算法的直观理解策略梯度算法REINFORCE算法基于平稳分布的策略梯度算法REINFORCE算法实现策略函数设计模型设计更新函数设计 练习总结 基于价…

注解实现校验接口传参是否超出取值范围

文章目录 1、定义注解2、使用注解3、其余校验实现思路2.04、其余校验实现思路3.0 写接口&#xff0c;Dto里很多字段要检验传参范围&#xff0c;自定义个注解来校验。 1、定义注解 注解定义代码&#xff1a; import javax.validation.Constraint; import javax.validation.Con…

2023年12月 电子学会 青少年软件编程等级考试Scratch三级真题

202312 青少年软件编程等级考试Scratch三级真题 一、单项题 第 1 题 运行左图程序&#xff0c;想得到右图中的效果&#xff0c;红色框应填写的数值是&#xff1f;&#xff08; &#xff09; A&#xff1a;12 B&#xff1a;11 C&#xff1a;10 D&#xff1a;9 第 2 题 下列…

网站转小程序系统,任意网址打包成小程序

源码介绍 将任意网站打包成小程序&#xff0c;只需简单修改域名&#xff0c;即可轻松实现&#xff01;这一创新技术让您的网站内容在小程序平台上焕发新生。通过智能转换工具&#xff0c;您可以将任意网站迅速转化为小程序&#xff0c;无需繁琐的编码和开发工作。只需简单修改…

小程序学习-19

Vant Weapp - 轻量、可靠的小程序 UI 组件库 ​​​​​ Vant Weapp - 轻量、可靠的小程序 UI 组件库 安装出现问题&#xff1a;rollbackFailedOptional: verb npm-session 53699a8e64f465b9 解决办法&#xff1a;http://t.csdnimg.cn/rGUbe Vant Weapp - 轻量、可靠的小程序…

微服务不死 — 共享变量在策略引擎项目的落地详解

01 背景 1、共享变量的提出 前段时间&#xff0c;来自亚马逊 Prime Video 团队的一个案例研究在开发者社区中掀起了轩然大波。大体是这样一件事&#xff0c;作为一个流媒体平台&#xff0c;Prime Video每天都会向客户提供成千上万的直播流。为了确保客户无缝接收内容&#xff0…

多人在线聊天交友工具,匿名聊天室网站源码,附带搭建教程

源码介绍 匿名聊天室&#xff08;nodejs vue&#xff09; 多人在线聊天交友工具&#xff0c;无需注册即可畅所欲言&#xff01;你也可以放心讲述自己的故事&#xff0c;说出自己的秘密&#xff0c;因为谁也不知道对方是谁。 运行说明 安装依赖项&#xff1a;npm install 启动…

SpringBoot整合Dubbo和Zookeeper分布式服务框架使用的入门项目实例

文章目录 SpringBoot整合Dubbo和Zookeeper分布式服务框架使用的入门项目实例Dubbo定义其核心部分包含: 工作原理为什么要用dubbo各个节点角色说明&#xff1a;调用关系说明&#xff1a; dubbo为什么需要和zookeeper结合使用&#xff0c;zookeeper在dubbo体系中起到什么作用&…

Chatgpt+Comfyui绘图源码说明及本地部署文档

其他文档地址&#xff1a; ChatgptComfyui绘图源码运营文档 ChatgptComfyui绘图源码线上部署文档 一、源码说明 1、源码目录说明 app_home&#xff1a;app官网源码chatgpt-java&#xff1a;管理后台服务端源码、用户端的服务端源码chatgpt-pc&#xff1a;电脑网页前端源码cha…

论文阅读笔记AI篇 —— Transformer模型理论+实战 (四)

论文阅读笔记AI篇 —— Transformer模型理论实战 &#xff08;四&#xff09; 一、理论1.1 理论研读1.2 什么是AI Agent? 二、实战2.1 先导知识2.1.1 tensor的创建与使用2.1.2 PyTorch的模块2.1.2.1 torch.nn.Module类的继承与使用2.1.2.2 torch.nn.Linear类 2.2 Transformer代…

YOLOv5改进 | 主干篇 | 华为GhostnetV1一种移动端的专用特征提取网络

一、本文介绍 本文给大家带来的改进机制是华为移动端模型Ghostnetv1,华为GhostnetV1一种移动端的专用特征提取网络,旨在在计算资源有限的嵌入式设备上实现高性能的图像分类。GhostNet的关键思想在于通过引入Ghost模块,以较低的计算成本增加了特征图的数量,从而提高了模型的…

一、用户管理中心——前端初始化

一、Ant Design Pro初始化 1.创建空文件夹 2.打开Ant Design Pro官网 3.打开终端进行初始化 在终端输入npm i ant-design/pro-cli -g 在终端输入pro create myapp 选择umi3 选择simple 项目创建成功后&#xff0c;在文件夹中出现myapp 4.安装依赖 使用vscode打开项目 …

Java学习笔记(八)——Lambda表达式

文章目录 Lambda表达式Lambda表达式的省略写法Lambda练习练习1练习2 算法题算法题1 斐波那契数列算法题2 猴子吃桃子算法题3 爬楼梯 Lambda表达式 Lambda表达式是JDK8开始的一种新语法形式。 基本作用&#xff1a;简化函数式接口的匿名内部类的写法。 注意&#xff1a; Lam…

lambda

文章目录 lambda 概述lambda的演变过程lambda 表达式的基本格式案例&#xff1a;调用接口里面的方法几种方式 lambda省略写法案例一&#xff1a;抽象方法一个参数抽象方法两个参数 啦么大 使用的注意事项啦么大 与 匿名内部类 lambda 概述 函数式编程思想 面向对象思想在乎的是…

Java 面向对象02 封装 (黑马)

人画圆&#xff1a;画圆这个方法应该定义在园这个类里面。 人关门&#xff1a;是人给了门一个作用力&#xff0c;然后门自己关上了门&#xff0c;所以关门的方法是在门的类里面 封装对象的好处&#xff1a; 调用Java自带的方法举例实现&#xff1a; 在测试类中&#xff0c;对其…

PDshell16逆向PostgreSQL 工程显示字段comment备注

现状&#xff1a;当刚逆向成功的表结构是没有原来表结构中的&#xff0c;comment备注如下 然后pd逆向工程的sql已经返回了这个备注的含义 解决方案&#xff1a; 1、设置显示注释列 tools——Display Preferences…如下 勾选-按照下面得方式勾选这三个 复制这里的VBS脚本&a…

触摸屏监控双速电动机-确定地址分配

I/O地址分配 当选择了PLC之后&#xff0c;首先需要确定的是系统中各I/O点的绝对地址。在某些PLC 中1/O绝对地址的分配方式共有固定地址型、自动分配型、用户定义型3种。实际所使用的方式取决于所采用的PLC的CPU型号、编程软件、软件版本、编程人员的选择等因素。 本任务输入信…

51单片机原理及应用张毅刚版课后习题以及答案

AT89S51单片机内部集成了哪些外围功能部件 ①8位微处理器CPU ②数据存储器 128B RAM ③程序存储器 ④4个8位可编程并行I/O口 ⑤1个全双工的异步串行口 ⑥2个可编程的16位定时器/计数器 ⑦1个看门狗定时器WDT ⑧中断系统具有五个中断源 五个中断向量 ⑨特殊功能寄存器SFR 26个…

低代码技术杂谈

一、探讨低代码的定义 “Low-Code”是什么&#xff1f;身为技术人员听到这种技术名词&#xff0c;咱们第一反应就是翻看维基百科 或者其他相关技术论文&#xff0c;咱们想看维基百科的英文介绍&#xff1a; A low-code development platform (LCDP) provides a development env…

web蓝桥杯真题--11、蓝桥知识网

介绍 蓝桥为了帮助大家学习&#xff0c;开发了一个知识汇总网站&#xff0c;现在想设计一个简单美观的首页。本题请根据要求来完成一个首页布局。 准备 开始答题前&#xff0c;需要先打开本题的项目代码文件夹&#xff0c;目录结构如下&#xff1a; ├── css │ └──…