当前位置: 首页 > 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…...

条件运算符

C中的三目运算符&#xff08;也称条件运算符&#xff0c;英文&#xff1a;ternary operator&#xff09;是一种简洁的条件选择语句&#xff0c;语法如下&#xff1a; 条件表达式 ? 表达式1 : 表达式2• 如果“条件表达式”为true&#xff0c;则整个表达式的结果为“表达式1”…...

django filter 统计数量 按属性去重

在Django中&#xff0c;如果你想要根据某个属性对查询集进行去重并统计数量&#xff0c;你可以使用values()方法配合annotate()方法来实现。这里有两种常见的方法来完成这个需求&#xff1a; 方法1&#xff1a;使用annotate()和Count 假设你有一个模型Item&#xff0c;并且你想…...

ABAP设计模式之---“简单设计原则(Simple Design)”

“Simple Design”&#xff08;简单设计&#xff09;是软件开发中的一个重要理念&#xff0c;倡导以最简单的方式实现软件功能&#xff0c;以确保代码清晰易懂、易维护&#xff0c;并在项目需求变化时能够快速适应。 其核心目标是避免复杂和过度设计&#xff0c;遵循“让事情保…...

C++.OpenGL (20/64)混合(Blending)

混合(Blending) 透明效果核心原理 #mermaid-svg-SWG0UzVfJms7Sm3e {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-SWG0UzVfJms7Sm3e .error-icon{fill:#552222;}#mermaid-svg-SWG0UzVfJms7Sm3e .error-text{fill…...

Windows安装Miniconda

一、下载 https://www.anaconda.com/download/success 二、安装 三、配置镜像源 Anaconda/Miniconda pip 配置清华镜像源_anaconda配置清华源-CSDN博客 四、常用操作命令 Anaconda/Miniconda 基本操作命令_miniconda创建环境命令-CSDN博客...

Rust 开发环境搭建

环境搭建 1、开发工具RustRover 或者vs code 2、Cygwin64 安装 https://cygwin.com/install.html 在工具终端执行&#xff1a; rustup toolchain install stable-x86_64-pc-windows-gnu rustup default stable-x86_64-pc-windows-gnu ​ 2、Hello World fn main() { println…...

安卓基础(Java 和 Gradle 版本)

1. 设置项目的 JDK 版本 方法1&#xff1a;通过 Project Structure File → Project Structure... (或按 CtrlAltShiftS) 左侧选择 SDK Location 在 Gradle Settings 部分&#xff0c;设置 Gradle JDK 方法2&#xff1a;通过 Settings File → Settings... (或 CtrlAltS)…...

uniapp 集成腾讯云 IM 富媒体消息(地理位置/文件)

UniApp 集成腾讯云 IM 富媒体消息全攻略&#xff08;地理位置/文件&#xff09; 一、功能实现原理 腾讯云 IM 通过 消息扩展机制 支持富媒体类型&#xff0c;核心实现方式&#xff1a; 标准消息类型&#xff1a;直接使用 SDK 内置类型&#xff08;文件、图片等&#xff09;自…...

java高级——高阶函数、如何定义一个函数式接口类似stream流的filter

java高级——高阶函数、stream流 前情提要文章介绍一、函数伊始1.1 合格的函数1.2 有形的函数2. 函数对象2.1 函数对象——行为参数化2.2 函数对象——延迟执行 二、 函数编程语法1. 函数对象表现形式1.1 Lambda表达式1.2 方法引用&#xff08;Math::max&#xff09; 2 函数接口…...

文件上传漏洞防御全攻略

要全面防范文件上传漏洞&#xff0c;需构建多层防御体系&#xff0c;结合技术验证、存储隔离与权限控制&#xff1a; &#x1f512; 一、基础防护层 前端校验&#xff08;仅辅助&#xff09; 通过JavaScript限制文件后缀名&#xff08;白名单&#xff09;和大小&#xff0c;提…...