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

Python 数据结构 4.单向链表

惟愿春日不迟,相逢终有时

                                            —— 25.3.2

一、单向链表的基本概念

1.单向链表的概念

        对于顺序存储的结构,最大的缺点就是:插入删除 的时候需要移动大量的元素,所以基于前人的智慧,他们发明了链表。

        链表是由一个个结点组成,每个结点之间通过链接关系串联起来,每个结点都有一个后继结点,最后一个结点的后继结点为空结点,如图所示:

        由链接关系 A ->B 组织起来的两个结点,B 被称为 A的后继结点,A 被称为 B 的前驱结点。链表分为:单向链表、双向链表、循环链表等等。

        一个链表结点由两部分组成:数据域指针域。数据可以是任意类型,由编码的人自行指定。指针域 指向 后继结点 的地址。一个结点包含的两部分,如下图所示:


2.单向链表的元素插入

        单向链表的元素插入,就是指给定一个索引 i 和一个元素 data,生成一个值为 data 的结点,并且插入到第i个位置上。

元素插入的步骤

第1步:判断插入位置是否合法,如果不合法则抛出异常(比如:原本只有5个元素,给定的索引是100,那显然这个位置是不合法的)

第2步:对给定的元素,生成一个链表结点。

第3步:如果插入位置是 0,则直接把生成的结点的后继结点,设置为当前的链表头结点,并且把生成的结点设置为新的链表头,

第4步:如果插入位置不是 0,则遍历到插入位置的前一个位置,把生成的结点插入进来

第5步:更新链表的大小,即对链表的元素执行加一操作。


3.单向链表的元素删除

        单向链表的元素删除,就是指给定一个索引 i,将从链表头开始数到的第 i 个结点删除。

元素删除的步骤

第1步:判断删除位置是否合法,如果不合法则抛出异常。

第2步:如果删除位置为首个结点,直接把链表头更新为它的后继结点,

第3步:如果删除位置非首个结点,则遍历到要删除位置的前一个结点,并且把前一个结点的后继结点设置为它后继的后继。

第4步:更新链表的大小,也就是将链表的大小执行减一操作。


4.单向链表的元素查找

        单向链表的元素查找,是指在链表中查找指定元素 x 是否存在,如果存在则返回该结点,否则返回 null。由于需要遍历整个链表进行元素对比,所以查找的时间复杂度为 (n)。

元素查找的步骤

第1步:遍历整个链表,对链表中的每个元素,和指定元素进行比较,如果相等则返回当前遍历到的结点;

第2步:如果遍历完整个链表,都没有找到相等的元素,则返回 NULL;


5.单向链表的元素索引

        单向链表的元素索引,是指给定一个索引值 i,从链表头结点开始数,数到第 i 个结点并且返回它,时间复杂度 O(n)。

元素索引的步骤

第1步:首先判断给定的索引是否合法,不合法就抛出异常

第2步:直接通过索引访问即可获得对应的元素;


6.单向链表的元素修改

        单向链表的元素修改是指将链表中指定索引的元素更新为新的值。

元素修改的步骤

第1步:直接通过索引访问即可获得对应的结点,修改成指定的值;


二、Python中的单向链表

class ListNode:def __init__(self, x):self.val = xself.next = Noneclass LinkedList:def __init__(self):self.head = Noneself.len = 0def size(self):return self.lendef insert(self, pos, val):if pos < 0 or pos > self.len:raise ValueError("index out of range")new_node = ListNode(val)if pos == 0:new_node.next = self.headself.head = new_nodeelse:prev = self.headfor _ in range(pos - 1):prev = prev.nextnew_node.next = prev.nextprev.next = new_nodeself.len += 1def delete(self, pos):if pos < 0 or pos >= self.size():raise ValueError("index out of range")if pos == 0:self.head = self.head.nextelse:prev = self.headfor _ in range(pos - 1):prev = prev.nextprev.next = prev.next.nextself.len -= 1def update(self, pos, val):if pos < 0 or pos >= self.size():raise ValueError("index out of range")curr = self.headfor _ in range(pos):curr = curr.nextcurr.val = valdef search(self, val):curr = self.headwhile curr:if curr.val == val:return currcurr = curr.nextreturn Nonedef index(self, val):index = 0curr = self.headwhile curr:if curr.val == val:return indexcurr = curr.nextindex += 1return -1def print(self):curr = self.headwhile curr:print(curr.val, end=" -> ")curr = curr.nextprint(None)def Test():list = LinkedList()list.insert(0, 1)list.print()list.insert(1, 1)list.print()list.insert(2, 4)list.print()list.insert(0, 1)list.print()list.insert(1, 1)list.print()list.insert(2, 4)list.print()list.update(2, 5)list.print()list.delete(2)list.print()list.insert(2, 4)list.print()list.update(4, 1)list.print()node = list.search(4)if node:print("Node found:", node.val)else:print("Node not found")x = list.index(4)print("Index of 4:", x)x = list.index(5)print("Index of 5:", x)Test()


三、单向链表实战

1.面试题 02.02. 返回倒数第 k 个节点

实现一种算法,找出单向链表中倒数第 k 个节点。返回该节点的值。

注意:本题相对原题稍作改动

示例:

输入: 1->2->3->4->5 和 k = 2
输出: 4

说明:

给定的 k 保证是有效的。

快慢指针法

思路与算法

① 初始化两个指针fast 和 slow,它们都指向链表的头节点 head

② 移动快指针:让 fast 指针先向前移动 k 步。

③ 同步移动快慢指针:当 fast 指针移动到链表的末尾时,slow 指针正好指向倒数第 k 个节点。

④ 返回结果:返回 slow 指针所指向节点的值。

# Definition for singly-linked list.
# class ListNode:
#     def __init__(self, val=0, next=None):
#         self.val = val
#         self.next = next
class Solution:def kthToLast(self, head: Optional[ListNode], k: int) -> int:fast = headslow = headwhile k > 0:fast = fast.nextk -= 1while fast:fast = fast.nextslow = slow.nextreturn slow.val


2.83. 删除排序链表中的重复元素

给定一个已排序的链表的头 head , 删除所有重复的元素,使每个元素只出现一次 。返回 已排序的链表 。

示例 1:

输入:head = [1,1,2]
输出:[1,2]

示例 2:

输入:head = [1,1,2,3,3]
输出:[1,2,3]

提示:

  • 链表中节点数目在范围 [0, 300] 内
  • -100 <= Node.val <= 100
  • 题目数据保证链表已经按升序 排列 

方法一 双指针判断

思路与算法

① 初始化指针curr 指针从链表的头节点 head 开始。prev 指针初始化为 None,用于记录当前节点的前一个节点。

② 遍历链表:外层 while 循环用于遍历整个链表,直到 curr 为 None。内层 while 循环用于处理当前节点 curr 与前一个节点 prev 值相同的情况。如果发现重复,则将 prev.next 指向 curr.next,从而跳过重复的节点。

③ 更新指针:如果没有发现重复,则更新 prev 为当前节点 curr,并将 curr 移动到下一个节点。​

④ 返回结果:最终返回处理后的链表头节点 head

# Definition for singly-linked list.
# class ListNode:
#     def __init__(self, val=0, next=None):
#         self.val = val
#         self.next = next
class Solution:def deleteDuplicates(self, head: Optional[ListNode]) -> Optional[ListNode]:curr = headprev = Nonewhile curr:while prev != None and curr.val == prev.val:prev.next = curr.nextcurr = prev.nextif curr == None:breakif curr == None:breakprev = currcurr = curr.nextreturn head


方法二 一次遍历进行判断

思路与算法

① ​初始化curr指针指向链表的头节点head

② 空链表处理:如果链表为空(curr == None),直接返回head

​③ 遍历链表:如果当前节点的值curr.val与下一个节点的值curr.next.val相同,则通过curr.next = curr.next.next删除下一个节点。如果值不相同,则移动curr指针到下一个节点。

④ 返回结果:最终返回处理后的链表头节点head

# Definition for singly-linked list.
# class ListNode:
#     def __init__(self, val=0, next=None):
#         self.val = val
#         self.next = next
class Solution:def deleteDuplicates(self, head: Optional[ListNode]) -> Optional[ListNode]:curr = headif curr == None:return headwhile curr.next:if curr.val == curr.next.val:curr.next = curr.next.nextelse:curr = curr.nextreturn head


3.面试题 02.01. 移除重复节点

编写代码,移除未排序链表中的重复节点。保留最开始出现的节点。

示例1:

 输入:[1, 2, 3, 3, 2, 1]
 输出:[1, 2, 3]

示例2:

 输入:[1, 1, 1, 1, 2]
 输出:[1, 2]

提示:

  1. 链表长度在[0, 20000]范围内。
  2. 链表元素在[0, 20000]范围内。

方法一、哈希表标记法

思路与算法

① 哈希表标记法​:利用数组模拟哈希表(大小20001),用于记录节点值是否已存在。数组下标对应节点值,元素值为1表示该值已出现过

② 双指针遍历:

        tmp指针:指向当前已去重链表的尾节点,用于连接新节点。

   curr指针:遍历原链表,检查当前节点是否重复。

③ 边界处理:​若链表为空直接返回;初始化时先将头节点加入哈希表,避免后续判空操作

# Definition for singly-linked list.
# class ListNode:
#     def __init__(self, val=0, next=None):
#         self.val = val
#         self.next = next
class Solution:def removeDuplicateNodes(self, head: Optional[ListNode]) -> Optional[ListNode]:if head == None:return None# 哈希表hash = [0 for i in range(20001)]tmp = headcurr = head.nexthash[head.val] = 1while curr:if hash[curr.val] == 0:hash[curr.val] = 1tmp.next = currtmp = tmp.nextcurr = curr.nexttmp.next = Nonereturn head


方法二、字典

思路与算法

初始化字典:首先,代码创建了一个空字典 dict,用于存储已经出现过的节点值。如果链表为空(head == None),则直接返回 None,因为空链表不需要去重。​

② 处理头节点:将头节点的值 head.val 存入字典,表示该值已经出现过。

③ 遍历链表:使用 curr 指针遍历链表,初始时 curr 指向头节点。在遍历过程中,检查 curr.next 的值是否已经存在于字典中:如果 curr.next.val 不在字典中,说明该值尚未出现过,将其存入字典,并将 curr 指针移动到下一个节点。如果 curr.next.val 已经在字典中,说明该值是重复的,跳过该节点,即将 curr.next 指向 curr.next.next,从而删除重复节点。

④ 返回结果:遍历结束后,返回去重后的链表头节点 head

# Definition for singly-linked list.
# class ListNode:
#     def __init__(self, val=0, next=None):
#         self.val = val
#         self.next = next
class Solution:def removeDuplicateNodes(self, head: Optional[ListNode]) -> Optional[ListNode]:dict = {}if head == None:return Nonedict[head.val] = 1curr = headwhile curr.next:if curr.next.val not in dict:dict[curr.next.val] = 1curr = curr.nextelse:curr.next = curr.next.next  return head


四、单向链表的应用

链表相比于顺序表的优点在于:对于给定的结点,删除操作优于顺序表

MMO游戏开发 —— AOI(Area of Interest)

简单来说,每个玩家只关心他周围的玩家的数据同步,而不关心整个世界的数据,有一种经典的实现方式:双向十字链表

所有玩家被串联在一个十字链表上,玩家移动其实就是链表上节点交换位置的过程,每个玩家想获取其他玩家的数据,只需要在十字链表上进行遍历即可,而服务器同步给客户端的数据,受AOI控制,所以可以根据客户端实际能够承受的性能,调整AOI的半径

通过有效的实现AOI技术,游戏开发中可以:

① 减少服务器负载 ② 降低网络延迟 ③ 提升游戏性能 ④ 增强玩家用户体验

 

相关文章:

Python 数据结构 4.单向链表

惟愿春日不迟&#xff0c;相逢终有时 —— 25.3.2 一、单向链表的基本概念 1.单向链表的概念 对于顺序存储的结构&#xff0c;最大的缺点就是&#xff1a;插入 和 删除 的时候需要移动大量的元素&#xff0c;所以基于前人的智慧&#xff0c;他们发明了链表。 链表是由一个个结…...

LeeCode题库第四十题

40.组合总和II 项目场景&#xff1a; 给定一个候选人编号的集合 candidates 和一个目标数 target &#xff0c;找出 candidates 中所有可以使数字和为 target 的组合。 candidates 中的每个数字在每个组合中只能使用 一次 。 注意&#xff1a;解集不能包含重复的组合。 示…...

AI日报 - 2025年3月2日 - 推特版

AI日报 - 2025年3月2日 - 推特版 &#x1f31f; 今日概览&#xff08;60秒速览&#xff09; ▎&#x1f916; AGI突破 | Anthropic预测AGI将于2027年实现 &#x1f52c; Sholto Douglas加入团队&#xff0c;开源社区推动AGI竞赛加速 ▎&#x1f4bc; 商业动向 | 腾讯发布Hunyua…...

Kotlin语言特性(一):空安全、扩展函数与协程

Kotlin语言特性&#xff08;一&#xff09;&#xff1a;空安全、扩展函数与协程 一、引言 Kotlin作为Android官方推荐的开发语言&#xff0c;相比Java具有诸多现代化特性。本文将重点介绍Kotlin三个最具特色的语言特性&#xff1a;空安全、扩展函数和协程&#xff0c;并结合A…...

玩转大模型——deepseek本地部署与ollama 非C盘安装之ChatBox配置

文章目录 ollama安装ollama是什么DeepSeek是什么下载地址非C盘安装配置大模型目录大模型下载安装deepseek-r1:1.5b安装deepseek-r1:7b ChatBox安装参考资料 ollama安装 ollama是什么 Ollama 是一个专注于本地运行大型语言模型的工具。它允许用户在本地环境中部署和运行各种开…...

面试题:说一下你对DDD的了解?

面试题:说一下你对DDD的了解? 在面试中,关于 DDD(领域驱动设计,Domain-Driven Design) 的问题是一个常见的技术考察点。DDD 是一种软件设计方法论,旨在通过深入理解业务领域来构建复杂的软件系统。以下是一个清晰、详细的回答模板,帮助你在面试中脱颖而出: DDD 的定义…...

【构建企业级Spring Boot应用:从基础到高级的全面指南】

摘要 本文旨在为开发者提供一份详尽的指南&#xff0c;帮助大家深入理解并掌握如何使用Spring Boot框架来快速开发企业级应用程序。通过实际案例分析、代码示例以及架构设计思路分享&#xff0c;读者不仅能够学习到理论知识&#xff0c;还能获得宝贵的实践经验。本文将涵盖从环…...

DAV_postgresql_3-schema

schem介绍&#xff1a; 什么是schema? 用户对象的集合叫做模式 不同模式下的对象可以同名 可以把用户下对象根据业务分类&#xff0c;不同的对象放在不同的模式 一个用户可以创与拥有多个模式 一个模式只能属于一个用户 普通用户创建模式需要授权指定数据库下的创建权限…...

Hive-04之存储格式、SerDe、企业级调优

一、主题 hive表的数据压缩和文件存储格式hive的自定义UDF函数hive的JDBC代码操作hive的SerDe介绍和使用hive的优化 二、要点 1. hive表的文件存储格式 Hive支持的存储数的格式主要有&#xff1a;TEXTFILE&#xff08;行式存储&#xff09; 、SEQUENCEFILE(行式存储)、ORC&…...

信号和槽

connect(信号发送者&#xff0c;发送的信号&#xff0c;信号接收者&#xff0c;信号的处理); 信号函数和槽函数的参数必须是一样的&#xff0c;但信号的参数可以多余槽函数的参数&#xff08;前面的参数类型必须一致&#xff09; 是控件和控件间的信号传递&#xff0c;这两个…...

从零开始用react + tailwindcss + express + mongodb实现一个聊天程序(八) 聊天框用户列表

简单画了个聊天框 就是咱们的HomePage.jsx 1.后端接口开发 在server/src/index.js 新增 messagesRoutes 先引入 import messageRoutes from ./routes/message.route.js // 消息接口 app.use(/api/messages, messageRoutes) 在routes文件夹下新建message.route.js 有3个路…...

关于后端使用Boolean或boolean时前端收到的参数的区别

当后端使用的是Boolean时&#xff0c;调用的方法是setIsLoginUser&#xff0c;前端收到的参数的参数名是isLoginUser 而当后端使用的是boolean时&#xff0c;调用的方法是setLoginUser&#xff0c;前端收到的参数的参数名是loginUser 封装类和基本数据类型在使用时需要注意这…...

智能称重搬物寻迹小车(论文+源码)

1 系统设计方案确定 本次设计的总系统有以下几个模块分别是避障模块&#xff0c;循迹模块&#xff0c;二维码扫描电路&#xff0c;称重电路&#xff0c;LCD显示电路和电机驱动模块&#xff0c;而且这几个模块都是由单片机stm32控制的&#xff0c;整个系统的框图如下图所示。其…...

使用 ASP.NET Core 创建和下载 zip 文件

对于最近的一个功能&#xff0c;我必须从用 ASP.NET Core 编写的内部网站下载一批文件。在下载文件之前对其进行压缩&#xff0c;结果证明这是一种轻松实现多文件下载的好方法。.NET 提供了所有需要的功能&#xff0c;在本文中&#xff0c;我将向您展示如何实现它。 首先&#…...

“深入浅出”系列之音视频开发:(12)使用FFmpeg实现倍速播放:技术细节与优化思路

一、前言 在音视频处理领域&#xff0c;倍速播放是一个常见的需求&#xff0c;尤其是在视频播放器、在线教育平台等场景中&#xff0c;用户常常需要以不同的速度播放视频内容。然而&#xff0c;实现一个高质量的倍速播放功能并不容易&#xff0c;尤其是在处理音频时&#xff0…...

dify绑定飞书多维表格

dify 绑定飞书和绑定 notion 有差不多的过程&#xff0c;都需要套一层应用的壳子&#xff0c;而没有直接可以访问飞书文档的 API。本文记录如何在dify工具中使用新增多条记录工具。 创建飞书应用 在飞书开放平台创建一个应用&#xff0c;个人用户创建企业自建应用。 自定义应…...

SQL server配置ODBC数据源(本地和服务器)

本地配置 1. 控制面板中找到系统ODBC数据源&#xff08;打开控制面板直接搜&#xff09; 2. 选择“系统DSN”&#xff0c;点击“添加” 3. 选择“SQL server” 4. 名称和描述自己填&#xff0c;服务器选择本机设备名称 5. 选择ID和密码验证&#xff0c;并填写本地SQL server登…...

LogiSim教程

一、LogiSim是什么 Logisim是一种设计数字电路的工具。 二、安装LogiSim 下载地址 https://sourceforge.net/projects/circuit/ 此软件需要java运行环境。 三、使用LogiSim &#xff08;一&#xff09;界面 Logisim界面分为菜单栏、工具栏、资源管理器&#xff0c;属性表…...

RAP: Efficient Text-Video Retrieval with Sparse-and-Correlated Adapter

​​标题&#xff1a;RAP:基于稀疏相关适配器的高效文本视频检索 原文链接&#xff1a;RAP: Efficient Text-Video Retrieval with Sparse-and-Correlated Adapter - ACL Anthology 发表&#xff1a;ACL-2024(NLP领域CCF A类) 摘要 文本-视频检索&#xff08;TVR&#xff0…...

I2C驱动(十一) -- gpio模拟的i2c总线驱动i2c-gpio.c分析

相关文章 I2C驱动(一) – I2C协议 I2C驱动(二) – SMBus协议 I2C驱动(三) – 驱动中的几个重要结构 I2C驱动(四) – I2C-Tools介绍 I2C驱动(五) – 通用驱动i2c-dev.c分析 I2C驱动(六) – I2C驱动程序模型 I2C驱动(七) – 编写I2C设备驱动之i2c_driver I2C驱动(八) – 编写I2C…...

不要升级,Flutter Debug 在 iOS 18.4 beta 无法运行,提示 mprotect failed: Permission denied

近期如果有开发者的 iOS 真机升级到 18.4 beta&#xff0c;大概率会发现在 debug 运行时会有 Permission denied 的相关错误提示&#xff0c;其实从 log 可以很直观看出来&#xff0c;就是 Dart VM 在初始化时&#xff0c;对内核文件「解释运行&#xff08;JIT&#xff09;」时…...

zjbdt

嵌入式软件工程师可以通过考取相关职业证书来提升专业能力和职业竞争力。以下是几种含金量较高且广受认可的证书&#xff1a; 1. NIEH 嵌入式技术工程师证书 颁发机构&#xff1a;教育部考试中心级别&#xff1a;初级、中级、高级内容&#xff1a;涵盖嵌入式系统的基础理论、开…...

【3天快速入门WPF】11-附加属性

目录 1. 步骤1:定义附加属性2. 示例代码3. 步骤2:在XAML中使用附加属性3.1. 示例代码4. 步骤3:扩展使用场景4.1. 示例代码5. 总结上一篇讲到了依赖属性,本篇主要想说一下附加属性。 在WPF中,附加属性(Attached Property)是一种特殊的依赖属性,允许你在不属于某个类的控…...

私有化部署大模型推理性能分析

从用户感知角度分析私有化部署的大模型推理性能&#xff0c;这里的用户感知包括响应速度、生成速度、系统可用性以及系统稳定性。大模型首先获取输入内容的字符串&#xff0c;将这部分内容转换为模型token,过模型推理,到最后输出第一个token的时间是ttft&#xff0c;从这以后&a…...

版图自动化连接算法开发 00001 ------ 直接连接两个给定的坐标点

版图自动化连接算法开发 00001 ------ 直接连接两个给定的坐标点 引言正文定义坐标点的类绘图显示代码直接连接两个坐标点引言 由于人工智能的加速普及,每次手动绘制版图都会觉得特别繁琐,作者本人在想可否搞一个自动化连接器件端口的算法,后期可以根据一些设定的限制进行避…...

UniApp 按钮组件 open-type 属性详解:功能、场景与平台差异

文章目录 引言一、open-type 基础概念1.1 核心作用1.2 通用使用模板 二、主流 open-type 值详解2.1 contact - 客服会话功能说明平台支持代码示例 2.2 share - 内容转发功能说明平台支持注意事项 2.3 getUserInfo - 获取用户信息功能说明平台支持代码示例 2.4 getPhoneNumber -…...

EtherCAT总线绝对值伺服如何使用

EtherCAT总线掉线如何自动重启。 EtherCAT总线掉线如何自动重启_ethercat从站断线-CSDN博客文章浏览阅读1.2k次。本文介绍了在EtherCAT通信中,当从站出现掉线情况时,如何通过设置自动重启功能来解决这一问题。详细步骤包括在CODESYS环境中启用从站的自动重启选项。https://r…...

可商用街头文化艺术海报封面手写涂鸦标题LOGO排版英文字体 FS163 TYPE FACE

Freestyle 163 &#xff08;FS163&#xff09;是一个受街头文化和城市艺术启发的视觉宣言。该字体旨在突出我们的文化和创意根源&#xff0c;反映了街头运动、城市艺术以及来自社会和边缘的故事。 FS163与面临挑战、质疑规范、放大被忽视声音的品牌和个人联系在一起&#xff0c…...

使用3090显卡部署Wan2.1生成视频

layout: post title: 使用3090显卡部署Wan2.1生成视频 catalog: true tag: [Kubernetes, GPU, AI] 使用3090显卡部署Wan2.1生成视频 1. 环境说明2. 模型下载3. 克隆仓库4. 安装依赖5. 生成视频 5.1. 使用generate脚本生成5.2. 使用gradio启动UI界面生成 5.2.1. 启动gradio服务5…...

js逆向常用代码

js逆向常用代码 加载 const loadingStyle #loadingDiv {position: fixed;z-index: 9999;top: 0;left: 0;width: 100%;height: 100%;background-color: rgba(255, 255, 255, 0.8);display: flex;align-items: center;justify-content: center;flex-direction: column;}.loade…...