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

【Python 数据结构 4.单向链表】

目录

一、单向链表的基本概念

1.单向链表的概念

2.单向链表的元素插入

元素插入的步骤

3.单向链表的元素删除

元素删除的步骤

4.单向链表的元素查找

元素查找的步骤

5.单向链表的元素索引

元素索引的步骤

6.单向链表的元素修改

元素修改的步骤

二、Python中的单向链表

​编辑

三、单向链表实战

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

快慢指针法

思路与算法

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

方法一 双指针判断

方法二 一次遍历进行判断

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

方法一、哈希表标记法

方法二、字典

四、单向链表的应用

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


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

                                            —— 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.单向链表】

目录 一、单向链表的基本概念 1.单向链表的概念 2.单向链表的元素插入 元素插入的步骤 3.单向链表的元素删除 元素删除的步骤 4.单向链表的元素查找 元素查找的步骤 5.单向链表的元素索引 元素索引的步骤 6.单向链表的元素修改 元素修改的步骤 二、Python中的单向链表 ​编辑 三…...

基于 vLLM 部署 LSTM 时序预测模型的“下饭”(智能告警预测与根因分析部署)指南

Alright,各位看官老爷们,准备好迎接史上最爆笑、最通俗易懂的 “基于 vLLM 部署 LSTM 时序预测模型的智能告警预测与根因分析部署指南” 吗? 保证让你笑出猪叫,看完直接变身技术大咖!🚀😂 咱们今天的主题,就像是要打造一个“智能运维小管家”! 这个小管家,不仅能提…...

Java多线程与高并发专题——ConcurrentHashMap 在 Java7 和 8 有何不同?

引入 上一篇我们提到HashMap 是线程不安全的&#xff0c;并推荐使用线程安全同时性能比较好的 ConcurrentHashMap。 而在 Java 8 中&#xff0c;对于 ConcurrentHashMap 这个常用的工具类进行了很大的升级&#xff0c;对比之前 Java 7 版本在诸多方面都进行了调整和变化。不过…...

NL2SQL-基于Dify+阿里通义千问大模型,实现自然语音自动生产SQL语句

本文基于Dify阿里通义千问大模型&#xff0c;实现自然语音自动生产SQL语句功能&#xff0c;话不多说直接上效果图 我们可以试着问他几个问题 查询每个部门的员工数量SELECT d.dept_name, COUNT(e.emp_no) AS employee_count FROM employees e JOIN dept_emp de ON e.emp_no d…...

LeetCode 1328.破坏回文串:贪心

【LetMeFly】1328.破坏回文串&#xff1a;贪心 力扣题目链接&#xff1a;https://leetcode.cn/problems/break-a-palindrome/ 给你一个由小写英文字母组成的回文字符串 palindrome &#xff0c;请你将其中 一个 字符用任意小写英文字母替换&#xff0c;使得结果字符串的 字典…...

计算机视觉|ViT详解:打破视觉与语言界限

一、ViT 的诞生背景 在计算机视觉领域的发展中&#xff0c;卷积神经网络&#xff08;CNN&#xff09;一直占据重要地位。自 2012 年 AlexNet 在 ImageNet 大赛中取得优异成绩后&#xff0c;CNN 在图像分类任务中显示出强大能力。随后&#xff0c;VGG、ResNet 等深度网络架构不…...

//定义一个方法,把int数组中的数据按照指定的格式拼接成一个字符串返回,调用该方法,并在控制台输出结果

import java.util.Scanner; public class cha{ public static void main(String[] args){//定义一个方法&#xff0c;把int数组中的数据按照指定的格式拼接成一个字符串返回&#xff0c;调用该方法&#xff0c;并在控制台输出结果//eg&#xff1a; 数组为&#xff1a;int[] arr…...

Python快捷手册

Python快捷手册 后续会陆续更新Python对应的依赖或者工具使用方法 文章目录 Python快捷手册[toc]1-依赖1-词云小工具2-图片添加文字3-BeautifulSoup网络爬虫4-Tkinter界面绘制5-PDF转Word 2-开发1-多线程和队列 3-运维1-Requirement依赖2-波尔实验室3-Anaconda3使用教程4-CentO…...

QT5 GPU使用

一、问题1 1、现象 2、原因分析 出现上图错误&#xff0c;无法创建EGL表面&#xff0c;错误&#xff1d;0x300b。申请不上native window有可能是缺少libqeglfs-mali-integration.so 这个库 3、解决方法 需要将其adb push 到小机端的/usr/lib/qt5/plugins/egldeviceintegrat…...

如何在Spring Boot中读取JAR包内resources目录下文件

精心整理了最新的面试资料和简历模板&#xff0c;有需要的可以自行获取 点击前往百度网盘获取 点击前往夸克网盘获取 以下是如何在Spring Boot中读取JAR包内resources目录下文件的教程&#xff0c;分为多种方法及详细说明&#xff1a; 方法1&#xff1a;使用 ClassPathResour…...

《张一鸣,创业心路与算法思维》

张一鸣&#xff0c;多年如一日的阅读习惯。 爱读人物传记&#xff0c;称教科书式人类知识最浓缩的书&#xff0c;也爱看心理学&#xff0c;创业以及商业管理类的书。 冯仑&#xff0c;王石&#xff0c;联想&#xff0c;杰克韦尔奇&#xff0c;思科。 《乔布斯传》《埃隆马斯…...

SSE 和 WebSocket 的对比

SSE 和 WebSocket 的对比 在现代Web开发中&#xff0c;实时通信是提升用户体验的重要手段。Server-Sent Events&#xff08;SSE&#xff09;和WebSocket是两种实现服务器与客户端之间实时数据传输的技术&#xff0c;但它们在功能、适用场景以及实现方式上有所不同。 1. 基本概…...

es如何进行refresh?

在 Elasticsearch 中,refresh 操作的作用是让最近写入的数据可以被搜索到。以下为你介绍几种常见的执行 refresh 操作的方式: 1. 使用 RESTful API 手动刷新 你可以通过向 Elasticsearch 发送 HTTP 请求来手动触发 refresh 操作。可以针对单个索引、多个索引或者所有索引进…...

Kubespray部署企业级高可用K8S指南

目录 前言1 K8S集群节点准备1.1 主机列表1.2 kubespray节点python3及pip3准备1.2.1. 更新系统1.2.2. 安装依赖1.2.3. 下载Python 3.12源码1.2.4. 解压源码包1.2.5. 编译和安装Python1.2.6. 验证安装1.2.7. 设置Python 3.12为默认版本&#xff08;可选&#xff09;1.2.8. 安装pi…...

【实战篇】【深度解析DeepSeek:从机器学习到深度学习的全场景落地指南】

一、机器学习模型:DeepSeek的降维打击 1.1 监督学习与无监督学习的"左右互搏" 监督学习就像学霸刷题——给标注数据(参考答案)训练模型。DeepSeek在信贷风控场景中,用逻辑回归模型分析百万级用户数据,通过特征工程挖掘出"凌晨3点频繁申请贷款"这类魔…...

优选算法的智慧之光:滑动窗口专题(二)

专栏&#xff1a;算法的魔法世界​​​​​​ 个人主页&#xff1a;手握风云 目录 一、例题讲解 1.1. 最大连续1的个数 III 1.2. 找到字符串中所有字母异位词 1.3. 串联所有单词的子串 1.4. 最小覆盖子串 一、例题讲解 1.1. 最大连续1的个数 III 题目要求是二进制数组&am…...

Kylin麒麟操作系统服务部署 | NFS服务部署

以下所使用的环境为&#xff1a; 虚拟化软件&#xff1a;VMware Workstation 17 Pro 麒麟系统版本&#xff1a;Kylin-Server-V10-SP3-2403-Release-20240426-x86_64 一、 NFS服务概述 NFS&#xff08;Network File System&#xff09;&#xff0c;即网络文件系统。是一种使用于…...

7.1.2 计算机网络的分类

文章目录 分布范围交换方式 分布范围 计算机网络按照分布范围可分为局域网、广域网、城域网。局域网的范围在10m~1km&#xff0c;例如校园网&#xff0c;网速高&#xff0c;主要用于共享网络资源&#xff0c;拓扑结构简单&#xff0c;约束少。广域网的范围在100km&#xff0c;例…...

Spring Cloud Alibaba 实战:轻松实现 Nacos 服务发现与动态配置管理

1. Nacos 介绍 1.1 什么是 Nacos&#xff1f; Nacos&#xff08;Naming and Configuration Service&#xff09;是阿里巴巴开源的一个服务注册中心和配置管理中心。它支持动态服务发现、配置管理和服务治理&#xff0c;适用于微服务架构&#xff0c;尤其是基于 Spring Cloud …...

【数据结构】LRUCache|并查集

目录 一、LRUCache 1.概念 2.实现:哈希表双向链表 3.JDK中类似LRUCahe的数据结构LinkedHashMap &#x1f525;4.OJ练习 二、并查集 1. 并查集原理 2.并查集代码实现 3.并查集OJ 一、LRUCache 1.概念 最近最少使用的&#xff0c;一直Cache替换算法 LRU是Least Recent…...

YSYX学习记录(八)

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

376. Wiggle Subsequence

376. Wiggle Subsequence 代码 class Solution { public:int wiggleMaxLength(vector<int>& nums) {int n nums.size();int res 1;int prediff 0;int curdiff 0;for(int i 0;i < n-1;i){curdiff nums[i1] - nums[i];if( (prediff > 0 && curdif…...

屋顶变身“发电站” ,中天合创屋面分布式光伏发电项目顺利并网!

5月28日&#xff0c;中天合创屋面分布式光伏发电项目顺利并网发电&#xff0c;该项目位于内蒙古自治区鄂尔多斯市乌审旗&#xff0c;项目利用中天合创聚乙烯、聚丙烯仓库屋面作为场地建设光伏电站&#xff0c;总装机容量为9.96MWp。 项目投运后&#xff0c;每年可节约标煤3670…...

ElasticSearch搜索引擎之倒排索引及其底层算法

文章目录 一、搜索引擎1、什么是搜索引擎?2、搜索引擎的分类3、常用的搜索引擎4、搜索引擎的特点二、倒排索引1、简介2、为什么倒排索引不用B+树1.创建时间长,文件大。2.其次,树深,IO次数可怕。3.索引可能会失效。4.精准度差。三. 倒排索引四、算法1、Term Index的算法2、 …...

Swagger和OpenApi的前世今生

Swagger与OpenAPI的关系演进是API标准化进程中的重要篇章&#xff0c;二者共同塑造了现代RESTful API的开发范式。 本期就扒一扒其技术演进的关键节点与核心逻辑&#xff1a; &#x1f504; 一、起源与初创期&#xff1a;Swagger的诞生&#xff08;2010-2014&#xff09; 核心…...

CSS设置元素的宽度根据其内容自动调整

width: fit-content 是 CSS 中的一个属性值&#xff0c;用于设置元素的宽度根据其内容自动调整&#xff0c;确保宽度刚好容纳内容而不会超出。 效果对比 默认情况&#xff08;width: auto&#xff09;&#xff1a; 块级元素&#xff08;如 <div>&#xff09;会占满父容器…...

LINUX 69 FTP 客服管理系统 man 5 /etc/vsftpd/vsftpd.conf

FTP 客服管理系统 实现kefu123登录&#xff0c;不允许匿名访问&#xff0c;kefu只能访问/data/kefu目录&#xff0c;不能查看其他目录 创建账号密码 useradd kefu echo 123|passwd -stdin kefu [rootcode caozx26420]# echo 123|passwd --stdin kefu 更改用户 kefu 的密码…...

苹果AI眼镜:从“工具”到“社交姿态”的范式革命——重新定义AI交互入口的未来机会

在2025年的AI硬件浪潮中,苹果AI眼镜(Apple Glasses)正在引发一场关于“人机交互形态”的深度思考。它并非简单地替代AirPods或Apple Watch,而是开辟了一个全新的、日常可接受的AI入口。其核心价值不在于功能的堆叠,而在于如何通过形态设计打破社交壁垒,成为用户“全天佩戴…...

Android写一个捕获全局异常的工具类

项目开发和实际运行过程中难免会遇到异常发生&#xff0c;系统提供了一个可以捕获全局异常的工具Uncaughtexceptionhandler&#xff0c;它是Thread的子类&#xff08;就是package java.lang;里线程的Thread&#xff09;。本文将利用它将设备信息、报错信息以及错误的发生时间都…...

土建施工员考试:建筑施工技术重点知识有哪些?

《管理实务》是土建施工员考试中侧重实操应用与管理能力的科目&#xff0c;核心考查施工组织、质量安全、进度成本等现场管理要点。以下是结合考试大纲与高频考点整理的重点内容&#xff0c;附学习方向和应试技巧&#xff1a; 一、施工组织与进度管理 核心目标&#xff1a; 规…...