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

news2024/11/26 20:40:01

链表理论基础

  • 链表是一种通过指针串连在一起的线性结构,每一个节点由两部分组成,一个是数据域,一个是指针域(存放指向下一个节点的指针)。最后一个节点的指针指向 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.coloradmin.cn/o/1399829.html

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

相关文章

TypeScript教程(一)在vscode中的配置TypeScript环境

TypeScript教程&#xff08;一&#xff09;在vscode中的配置TypeScript环境 文章目录 TypeScript教程&#xff08;一&#xff09;在vscode中的配置TypeScript环境一、前言二、具体步骤1、Node.js安装2、TypeScript安装3、helloworld 一、前言 未来的开发者们请上座&#xff0c…

EasyRecovery2024电脑数据恢复工具好不好用?

Ontrack是我们综述中的第一个产品&#xff0c;由于该软件的功效和广度&#xff0c;我认为它完全基于业务。有一个具有基本功能的免费版本和一系列付费版本&#xff0c;不仅可以恢复文件&#xff08;免费版和家庭版&#xff09;&#xff0c;还可以创建磁盘映像/从 CD 和 DVD 恢复…

如何自学Python:一份详细的指南

&#x1f4dd;个人主页&#xff1a;五敷有你 &#x1f525;系列专栏&#xff1a;有感而谈⛺️稳中求进&#xff0c;晒太阳 引言 Python是一种广泛使用的高级编程语言&#xff0c;以其简洁明了的语法和强大的功能而受到许多程序员的喜爱。无论是数据分析、网络开发&#…

基于JavaWeb+SSM+Vue基于微信小程序的在线投稿系统的设计和实现

基于JavaWebSSMVue基于微信小程序的在线投稿系统的设计和实现 滑到文末获取源码Lun文目录前言主要技术系统设计功能截图 滑到文末获取源码 Lun文目录 目录 1系统概述 1 1.1 研究背景 1 1.2研究目的 1 1.3系统设计思想 1 2相关技术 2 2.1微信小程序 2 2.2 MYSQL数据库 3 2.3 u…

11、Kafka ------ Kafka 核心API 及 生产者API 讲解

目录 Kafka核心API 及 生产者API讲解★ Kafka的核心APIKafka包含如下5类核心API&#xff1a; ★ 生产者APIKafka 的API 文档 ★ 使用生产者API发送消息 Kafka核心API 及 生产者API讲解 官方文档 ★ Kafka的核心API Kafka包含如下5类核心API&#xff1a; Producer API&#x…

ESP32-HTTP_webServer库(Arduino)

ESP32-HTTP 介绍 ESP32是一款功能强大的微控制器&#xff0c;具有丰富的网络和通信功能。其中之一就是支持HTTP协议&#xff0c;这使得ESP32可以用于创建Web服务器。 HTTP是什么&#xff1f; HTTP&#xff08;Hyper Text Transfer Protocol&#xff09;&#xff0c;即超文本传…

如何快速打开github

作为一个资深码农&#xff0c;怎么能不熟悉全球最大的同性交友社区——github呢&#xff0c;但头疼的是github有时能打开&#xff0c;有时打不开&#xff0c;这是怎么回事&#xff1f; 其实问题出在github.com解析DNS上&#xff0c;并不是需要FQ。下面提供一个方法&#xff0c;…

前端打同一个包可以从测试晋升到生产的配置方案

前端打同一个包从测试晋升到生产环境的方案&#xff0c;是一种高效、可靠且易于维护的部署方式。在这种方案中&#xff0c;前端代码在开发完成后&#xff0c;经过测试验证无误后&#xff0c;可以直接打包部署到生产环境&#xff0c;无需进行额外的配置或修改。这样可以减少部署…

HarmonyOS 应用开发入门

HarmonyOS 应用开发入门 前言 DevEco Studio Release版本为&#xff1a;DevEco Studio 3.1.1。 Compile SDK Release版本为&#xff1a;3.1.0&#xff08;API 9&#xff09;。 构建方式为 HVigor&#xff0c;而非 Gradle。 最新版本已不再支持 &#xff08;”Java、JavaScrip…

已解决Error:AttributeError: module ‘numpy‘ has no attribute ‘complex‘

文章目录 引言报错分析解决方案1&#xff1a;降低NumPy版本解决方案2&#xff1a;更改NumPy源码 结尾 引言 在Python编程的世界里&#xff0c;NumPy无疑占据着举足轻重的地位&#xff0c;它承担着处理大规模数值计算的重要任务。然而&#xff0c;与所有强大工具一样&#xff0…

晶振负载电容公式,怎么计算?

晶振&#xff0c;或称晶体振荡器&#xff0c;是电子设备中的关键元件&#xff0c;尤其在时序电路中扮演着重要角色。晶振的性能直接影响到整个电路的稳定性和精度。在晶振设计中&#xff0c;负载电容是一个重要的参数。理解如何计算负载电容对于正确配置和使用晶振至关重要。今…

Find My卡片正成为消费电子香饽饽,伦茨科技ST17H6x可以帮到您

今年CES许多公司发布支持苹果Find My的卡片产品&#xff0c;这种产品轻薄可充电&#xff0c;放在钱包、背包或者手提包可以防丢查找&#xff0c;在智能化加持下&#xff0c;防丢卡片使得人们日益关心自行车的去向。最新的防丢卡片与苹果Find My结合&#xff0c;智能防丢&#x…

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

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

Halcon 边缘提取

文章目录 算子Halcon edges_image 示例Halcon frei_amp 示例Halcon kirsch_amp示例Halcon sobel_amp示例Halcon sobel_amp 算子示例Halcon sobel_dir 算子示例Halcon close_edges关闭图像间隙示例Halcon close_edges_length关闭图像间隙示例 算子 edges_image 对于图像进行边缘…

64 C++对象模型探索。类对象占用空间问题分析

0.没有任何成员变量的空类&#xff0c;实际上占用空间为1字节。 这是因为当你的使用代码实例化一个类的时候&#xff0c;tea对象就有了空间&#xff0c;&tea是有地址的&#xff0c;那么你这个地址至少要占用1个字节才合理。 Teacher tea; 就像我们买房子&#xff0c;你的…

爬虫接口获取外汇数据(汇率,外汇储备,贸易顺差,美国CPI,M2,国债利率)

akshare是一个很好用的财经数据api接口&#xff0c;完全免费&#xff01;&#xff01;和Tushare不一样。 除了我标题显示的数据外&#xff0c;他还提供各种股票数据&#xff0c;债券数据&#xff0c;外汇&#xff0c;期货&#xff0c;宏观经济&#xff0c;基金&#xff0c;银行…

JDK 动态代理(Spring AOP 的原理)(面试重点)

代理模式 也叫委托模式.定义&#xff1a;为其他对象提供⼀种代理以控制对这个对象的访问.它的作⽤就是通过提供⼀个代理类,让我们 在调⽤⽬标⽅法的时候,不再是直接对⽬标⽅法进⾏调⽤,⽽是通过代理类间接调⽤&#xff0c;在某些情况下,⼀个对象不适合或者不能直接引⽤另⼀个对…

Stream toList不能滥用以及与collect(Collectors.toList())的区别

Stream toList()返回的是只读List原则上不可修改&#xff0c;collect(Collectors.toList())默认返回的是ArrayList,可以增删改查 1. 背景 在公司看到开发环境突然发现了UnsupportedOperationException 报错&#xff0c;想到了不是自己throw的应该就是操作collection不当。 发…

Jetbrains Writerside 使用教程

系列文章目录 前言 一、入门 Writerside 是基于 IntelliJ 平台的 JetBrains 集成开发环境。使用它可以编写、构建、测试和发布技术文档。 如果你想将 Writerside 作为另一个 JetBrains IDE 的插件&#xff0c;请参阅 Writerside 作为插件。 1.1 安装 Writerside&#xfeff;…

vue路由-全局前置守卫

1. 介绍 详见&#xff1a;全局前置守卫网址 使用场景&#xff1a; 对于支付页&#xff0c;订单页等&#xff0c;必须是登录的用户才能访问的&#xff0c;游客不能进入该页面&#xff0c;需要做拦截处理&#xff0c;跳转到登录页面 全局前置守卫的原理&#xff1a; 全局前置…