【数据结构基础_链表】
1、链表的定义
链表与数组的区分:
数组是一块连续的内存空间,有了这块内存空间的首地址,就能直接通过索引计算出任意位置的元素地址。
数组最大的优势是支持通过索引快速访问元素,而链表就不支持。链表不一样,一条链表并不需要一整块连续的内存空间存储元素。
链表的元素可以分散在内存空间的天涯海角,通过每个节点上的 next, prev 指针,将零散的内存块串联起来形成一个链式结构。1)这样可以提高内存的利用效率,链表的节点不需要挨在一起,给点内存 new 出来一个节点就能用,操作系统会觉得这娃好养活2)另外一个好处,它的节点要用的时候就能接上,不用的时候拆掉就行了,从来不需要考虑扩缩容和数据搬移的问题弊端:
因为元素并不是紧挨着的,所以如果你想要访问第 3 个链表元素,你就只能从头结点开始往顺着 next 指针往后找,直到找到第 3 个节点才行。
二、链表的类型
1、单链表
编程语言标准库一般都会提供泛型,即你可以指定 val 字段为任意类型,而力扣的单链表节点的 val 字段只有 int 类型。
class ListNode:def __init__(self, x):self.val = xself.next = None
2、双链表
编程语言标准库一般使用的都是双链表而非单链表。
单链表节点只有一个 next 指针,指向下一个节点;
而双链表节点有两个指针,prev 指向前一个节点,next 指向下一个节点。
class Node:def __init__(self, prev, element, next):self.val = elementself.next = next #指向下个元素的指针self.prev = prev #指向上个元素的指针
三、单链表的操作
首先,要创建一个单链表,来用于下面的操作
class ListNode:def __init__(self, x): # 修正了 __int__ 为 __init__self.val = xself.next = Nonedef createlinkedlist(arry: 'List[int]') -> ListNode:if arry is None or len(arry) == 0:return Nonehead = ListNode(arry[0]) # 创建头节点current = head # 使用一个指针来遍历链表for i in range(1, len(arry)):current.next = ListNode(arry[i]) # 创建新节点并链接current = current.next # 移动指针return head # 返回链表的头节点
1、对节点进行赋值,必须转化为ListNode类型head = ListNode(arry[0]) # 创建头节点current.next = ListNode(arry[i]) # 创建新节点并链接错误写法:current.next = arry[i]2、必须使用指针来遍历链表head = ListNode(arry[0]) # 创建头节点
current = head # 使用一个指针来遍历链表问题:为什么需要使用指针(如 current)?
1、保持链表头节点的引用
链表的头节点是链表的入口点,通常需要保持对它的引用,以便后续可以访问整个链表。如果不使用额外的指针,直接操作 head,可能会导致以下问题:
1)丢失链表头节点:在链表操作过程中,如果直接修改 head,可能会意外地丢失对链表的引用,导致无法再访问链表的其他部分。
2)无法返回链表头节点:在函数中创建链表时,最终需要返回链表的头节点。如果不使用额外的指针,直接操作 head,可能会导致返回的头节点指向错误的位置。2、方便链表的遍历和操作
使用指针(如 current)可以方便地遍历链表,并在遍历过程中对链表进行操作(如插入、删除节点)。指针的作用类似于一个“游标”,可以在不改变链表头节点的情况下,逐个访问链表的节点。3、如果不使用指针:
def createlinkedlist(arry: 'List[int]') -> ListNode:。。。head = ListNode(arry[0])for i in range(1, len(arry)):head.next = ListNode(arry[i])head = head.next # 直接修改 headreturn head # 返回的是最后一个节点,而不是头节点
运行结果:

1、查
# 创建一条单链表
head = createLinkedList([1, 2, 3, 4, 5])# P为指针,遍历单链表
p = head
while p is not None:print(p.val)p = p.next
2、增
在头部增加新节点
# 创建一条单链表
head = createLinkedList([1, 2, 3, 4, 5])# 在单链表头部插入一个新节点 0
newHead = ListNode(0)newHead.next = head
head = newHead
# 现在链表变成了 0 -> 1 -> 2 -> 3 -> 4 -> 5
在尾部增加新节点
# 创建一条单链表
head = createLinkedList([1, 2, 3, 4, 5])# 在单链表尾部插入一个新节点 6
p = head
# 先走到链表的最后一个节点
while p.next is not None:p = p.next
# 现在 p 就是链表的最后一个节点
# 在 p 后面插入新节点
p.next = ListNode(6)# 现在链表变成了 1 -> 2 -> 3 -> 4 -> 5 -> 6
在单链表中间插入新元素
# 创建一条单链表
head = createLinkedList([1, 2, 3, 4, 5])# 在第 3 个节点后面插入一个新节点 66
# 先要找到前驱节点,即第 3 个节点
p = head
for _ in range(2):p = p.next
# 此时 p 指向第 3 个节点
# 组装新节点的后驱指针
new_node = ListNode(66)
new_node.next = p.next# 插入新节点
p.next = new_node# 现在链表变成了 1 -> 2 -> 3 -> 66 -> 4 -> 5
3、删
删除一个节点,首先要找到要被删除节点的前驱节点,然后把这个前驱节点的 next 指针指向被删除节点的下一个节点。这样就能把被删除节点从链表中摘除了。
# 创建一条单链表
head = createLinkedList([1, 2, 3, 4, 5])# 删除第 4 个节点,要操作前驱节点
p = head
for i in range(2):p = p.next# 此时 p 指向第 3 个节点,即要删除节点的前驱节点
# 把第 4 个节点从链表中摘除
p.next = p.next.next# 现在链表变成了 1 -> 2 -> 3 -> 5
在单链表尾部删除元素
# 创建一条单链表
head = createLinkedList([1, 2, 3, 4, 5])# 删除尾节点
p = head
# 找到倒数第二个节点
while p.next.next is not None:p = p.next# 此时 p 指向倒数第二个节点
# 把尾节点从链表中摘除
p.next = None# 现在链表变成了 1 -> 2 -> 3 -> 4
在单链表头部删除元素
# 创建一条单链表
head = createLinkedList([1, 2, 3, 4, 5])# 删除头结点
head = head.next# 现在链表变成了 2 -> 3 -> 4 -> 5
四、双链表的操作
class DoublyListNode:def __init__(self, x):self.val = xself.next = Noneself.prev = Nonedef createDoublyLinkedList(arr: List[int]) -> Optional[DoublyListNode]:if not arr:return Nonehead = DoublyListNode(arr[0])cur = head# for 循环迭代创建双链表for val in arr[1:]:#基于切片进行迭代new_node = DoublyListNode(val)cur.next = new_nodenew_node.prev = curcur = cur.nextreturn head
判断空列表:方式一:更加显式if arr is None or len(arr):方式二:更加简洁if not arr:在 Python 中,if not arr 是一种简洁的写法,用于检查一个可迭代对象(如列表、字符串、字典等)是否为空。它基于 Python 的布尔上下文(Boolean Context):如果 arr 是 None 或者是一个空列表([]),if not arr 的条件为 True。如果 arr 是一个非空列表(如 [1, 2, 3]),if not arr 的条件为 False。因此,if not arr 可以同时检查 arr 是否为 None 或者是否为空列表。0为false,非0 为true
1、查
对于双链表的遍历和查找,我们可以从头节点或尾节点开始,根据需要向前或向后遍历:
# 创建一条双链表
head = createDoublyLinkedList([1, 2, 3, 4, 5])
tail = None# 1、从头节点向后遍历双链表
p = head
while p:print(p.val)tail = pp = p.next# 2、从尾节点向前遍历双链表
p = tail
while p:print(p.val)p = p.prev
2、增
在链表头节点插入一个值
# 创建一条双链表
head = create_doubly_linked_list([1, 2, 3, 4, 5])# 在双链表头部插入新节点 0
new_head = DoublyListNode(0)
new_head.next = head
head.prev = new_head
head = new_head
# 现在链表变成了 0 -> 1 -> 2 -> 3 -> 4 -> 5
在链表尾部插入一个值
# 创建一条双链表
head = createDoublyLinkedList([1, 2, 3, 4, 5])# p是一个指针,初始化是从头节点开始
p = head
# 先走到链表的最后一个节点
while p.next is not None:p = p.next# 在双链表尾部插入新节点 6
newNode = DoublyListNode(6)
p.next = newNode
newNode.prev = p
# 更新尾节点引用
p = newNode# 现在链表变成了 1 -> 2 -> 3 -> 4 -> 5 -> 6
在双链表中间插入元素
# 创建一条双链表
head = createDoublyLinkedList([1, 2, 3, 4, 5])# 在第 3 个节点后面插入新节点 66
# 找到第 3 个节点
p = head
for _ in range(2): #range(2)代表0,1p = p.next# 组装新节点
newNode = DoublyListNode(66)
newNode.next = p.next
newNode.prev = p# 插入新节点
p.next.prev = newNode
p.next = newNode# 现在链表变成了 1 -> 2 -> 3 -> 66 -> 4 -> 5
解释
_ 是一个特殊的变量名,通常被称为“占位符变量”for _ in range(2): 的作用
for _ in range(2): 的意思是:循环两次,每次循环中 _ 的值会从 range(2) 中依次取值(即 0 和 1),但 _ 的值在循环体中不会被使用。
3、删
在双链表中删除一个节点,需要调整前驱节点和后继节点的指针
# 创建一条双链表
head = createDoublyLinkedList([1, 2, 3, 4, 5])# 删除第 4 个节点
# 先找到第 3 个节点
p = head
for i in range(2):p = p.next# 现在 p 指向第 3 个节点,我们将它后面的那个节点摘除出去
toDelete = p.next# 把 toDelete 从链表中摘除
p.next = toDelete.next
toDelete.next.prev = p# 把 toDelete 的前后指针都置为 null 是个好习惯(可选)
toDelete.next = None
toDelete.prev = None# 现在链表变成了 1 -> 2 -> 3 -> 5
在双链表头部删除元素
# 创建一条双链表
head = createDoublyLinkedList([1, 2, 3, 4, 5])# 删除头结点
toDelete = head
head = head.next
head.prev = None# 清理已删除节点的指针
toDelete.next = None# 现在链表变成了 2 -> 3 -> 4 -> 5
在双链表尾部删除元素
# 创建一条双链表
head = createDoublyLinkedList([1, 2, 3, 4, 5])# 删除尾节点
p = head
# 找到尾结点
while p.next is not None:p = p.next# 现在 p 指向尾节点
# 把尾节点从链表中摘除
p.prev.next = None# 把被删结点的指针都断开是个好习惯(可选)
p.prev = None# 现在链表变成了 1 -> 2 -> 3 -> 4
防止内存泄漏:把删除的元素,赋值为None,就可以
相关文章:
【数据结构基础_链表】
1、链表的定义 链表与数组的区分: 数组是一块连续的内存空间,有了这块内存空间的首地址,就能直接通过索引计算出任意位置的元素地址。 数组最大的优势是支持通过索引快速访问元素,而链表就不支持。链表不一样,一条链…...
Java 实现 Redis中的GEO数据结构
Java 实现 Redis中的GEO数据结构 LBS (基于位置信息服务(Location-Based Service,LBS))应用访问的数据是和人 或物关联的一组经纬度信息,而且要能查询相邻的经纬度范围,GEO 就非常适合应用在 …...
PostgreSQL如何关闭自动commit
PostgreSQL如何关闭自动commit 在 PostgreSQL 中,默认情况下,每个 SQL 语句都会自动提交(即 AUTOCOMMIT 是开启的)。如果希望关闭自动提交,以便手动控制事务的提交和回滚,可以通过以下方法实现。 1 使用 …...
1、云原生写在前面
云原生技术是什么(包含哪些组件)?每个组件是负责什么?学习这些组件技术能解决什问题?哪些类企业需要用到? 这是标准系列的问题,通过 deepseek 的深度思考就能得到我们想要的易于理解的人话式的…...
Redis离线安装
Linux系统Centos安装部署Redis缓存插件 参考:Redis中文网: https://www.redis.net.cn/ 参考:RPM软件包下载地址: https://rpmfind.net/linux/RPM/index.html http://rpm.pbone.net/ https://mirrors.aliyun.com/centos/7/os…...
网络安全-攻击流程-应用层
应用层攻击针对OSI模型的第七层(应用层),主要利用协议漏洞、业务逻辑缺陷或用户交互弱点,直接威胁Web应用、API、数据库等服务。以下是常见应用层攻击类型及其流程,以及防御措施: 1. SQL注入(SQ…...
java八股文-spring
目录 1. spring基础 1.1 什么是Spring? 1.2 Spring有哪些优点? 1.3 Spring主要模块 1.4 Spring常用注解 1.5 Spring中Bean的作用域 1.6 Spring自动装配的方式 1.7 SpringBean的生命周期 1.8 多级缓存 1.9 循环依赖? 1 .8.1 原因 1.8…...
Jvascript网页设计案例:通过js实现一款密码强度检测,适用于等保测评整改
本文目录 前言功能预览样式特点总结:1. 整体视觉风格2. 密码输入框设计3. 强度指示条4. 结果文本与原因说明 功能特点总结:1. 密码强度检测2. 实时反馈机制3. 详细原因说明4. 视觉提示5. 交互体验优化 密码强度检测逻辑Html代码Javascript代码 前言 能满…...
【Scrapy】Scrapy教程2——工作原理
文章目录 数据流组件引擎Engine调度器Scheduler下载器Downloader爬虫Spiders项目管道Item Pipeline下载器中间件Downloader Middlewares爬虫中间件Spider Middlewares 在学习Scrapy前,我们需要先了解其架构和工作原理,这样才能很好的去使用Scrapy。 Scra…...
探索 DeepSeek:AI 领域的璀璨新星
在人工智能飞速发展的当下,DeepSeek 作为行业内的重要参与者,正以独特的技术和广泛的应用备受瞩目。 DeepSeek 是一家专注于实现 AGI(通用人工智能)的中国人工智能公司。它拥有自主研发的深度学习框架,能高效处理海量…...
宏基传奇swift edge偶尔开机BIOS重置
电脑是acer swift edge, SFA16-41,出厂是Win11系统, BIOS版本出厂1.04,更新到了目前最新1.10。 问题是 会偶尔开机ACER图标变小跑到屏幕左上方,下次开机BIOS就会被重置,开机等待很长时间。 因为是偶尔现象的…...
自动驾驶---如何打造一款属于自己的自动驾驶系统
在笔者的专栏《自动驾驶Planning决策规划》中,主要讲解了行车的相关知识,从Routing,到Behavior Planning,再到Motion Planning,以及最后的Control,笔者都做了相关介绍,其中主要包括算法在量产上…...
【C语言】第一期——数据类型变量常量
目录 1 字面量 2 整数类型 2.1 整数类型的取值范围 2.1.1 sizeof 运算符 2.2 GB、MB、KB、B之间的关系 2.3 定义整数类型的变量并打印 2.4 整数类型代码演示 3 浮点类型 3.1 浮点类型的取值范围 3.2 定义浮点类型变量并打印 3.3 保留2位小数点 4 char字符型 4.1…...
04运维实用篇(D4_日志)
目录 一、简介 二、代码中使用日志工具记录日志 1. 操作步骤 步骤1:添加日志记录操作 步骤2:设置日志输出级别 步骤3:设置日志组 2. 知识小结 三、优化日志对象创建代码 1. 实例 2. 总结 四、日志输出格式控制 1. 实例 2. 总结 …...
centos部署open-webui
提示:本文将简要介绍一下在linux下open-webui的安装过程,安装中未使用虚拟环境。 文章目录 一、open-webui是什么?二、安装流程1.openssl升级2.Python3.11安装3.sqlite安装升级4.pip 下载安装open-webui 总结 一、open-webui是什么? Open W…...
UE求职Demo开发日志#32 优化#1 交互逻辑实现接口、提取Bag和Warehouse的父类
1 定义并实现交互接口 接口定义: // Fill out your copyright notice in the Description page of Project Settings.#pragma once#include "CoreMinimal.h" #include "UObject/Interface.h" #include "MyInterActInterface.generated.h…...
Visonpro 检测是否有缺齿
一、效果展示 二、上面是原展开工具CogPolarUnwrapTool; 第二种方法: 用Blob 和 CogCopyRegionTool 三、 用预处理工具 加减常数,让图片变得更亮点 四、圆展开工具 五、模板匹配 六、代码分解 1.创建集合和文子显示工具 CogGraphicCollec…...
第1章大型互联网公司的基础架构——1.6 RPC服务
你可能在1.1节的引言中注意到业务服务层包括HTTP服务和RPC服务,两者的定位不一样。一般来说,一个业务场景的核心逻辑都是在RPC服务中实现的,强调的是服务于后台系统内部,所谓的“微服务”主要指的就是RPC服务;而HTTP服…...
今日AI和商界事件(2025-02-15)
根据2025年2月15日的科技动态,以下是今日AI领域的重要事件及相关进展总结: 1. DeepSeek日活突破3000万,开源生态加速AI普惠 里程碑意义:开源大模型DeepSeek宣布日活跃用户数突破3000万,其R1模型凭借开源策略和低成本优…...
算法题(69):搜索插入位置
审题: 需要我们在有序数组中找到等于target值的元素的下标若没有则返回target按顺序会插入的位置的索引 思路 : 我们可以使用二分查找的方法 方法一:二分查找 和普通的二分查找不同,本题若没有找到就需要返回它按顺序插入的位置的…...
小米手表表盘制作神器:3步搞定个性化设计,无需任何编程基础
小米手表表盘制作神器:3步搞定个性化设计,无需任何编程基础 【免费下载链接】Mi-Create Unofficial watchface creator for Xiaomi wearables ~2021 and above 项目地址: https://gitcode.com/gh_mirrors/mi/Mi-Create 你是不是也曾为小米手表上单…...
VCS NLP低功耗仿真避坑指南:从UPF文件加载到Verdi Debug的完整实战
VCS NLP低功耗仿真避坑指南:从UPF文件加载到Verdi Debug的完整实战 在数字IC验证领域,低功耗仿真已成为不可或缺的一环。随着工艺节点不断演进,芯片功耗问题日益凸显,动态功耗管理变得至关重要。VCS NLP(Native Low Po…...
Claude Code技能promptly-prompt:通过上下文工程提升AI编程协作效率
1. 项目概述:一个让AI先理解再执行的Claude Code技能 如果你用过Claude Code,或者任何AI编程助手,一定遇到过这种情况:你脑子里有个模糊的想法,噼里啪啦打了一长串指令过去,结果AI要么跑偏了方向࿰…...
企业级在线考试系统架构解决方案框架:学之思开源系统实施指南
企业级在线考试系统架构解决方案框架:学之思开源系统实施指南 【免费下载链接】xzs-mysql 学之思开源考试系统是一款 java vue 的前后端分离的考试系统。主要优点是开发、部署简单快捷、界面设计友好、代码结构清晰。支持web端和微信小程序,能覆盖到pc机…...
ESP32远程ID实战手册:构建合规无人机识别系统的全面指南
ESP32远程ID实战手册:构建合规无人机识别系统的全面指南 【免费下载链接】ArduRemoteID RemoteID support using OpenDroneID 项目地址: https://gitcode.com/gh_mirrors/ar/ArduRemoteID 随着全球无人机监管框架的日益完善,远程识别已成为商用无…...
体验 Taotoken 官方价折扣后模型调用的成本优化效果
体验 Taotoken 官方价折扣后模型调用的成本优化效果 1. 成本优化背景与观察方法 对于个人开发者或中小团队而言,大模型 API 的调用成本是技术选型时的重要考量因素。Taotoken 平台通过聚合多家模型供应商并提供统一接入点,在保持 OpenAI 兼容 API 的同…...
Pecker框架:时序电路缺陷定位的创新解决方案
1. 硬件缺陷定位的挑战与Pecker框架概述在芯片设计领域,缺陷定位一直是验证流程中最耗时费力的环节。据统计,硬件设计项目中超过60%的验证时间都消耗在缺陷定位上。传统基于频谱的缺陷定位技术(SBFL)虽然在软件工程领域取得了显著…...
开源监控告警平台PANIC:从架构到部署的完整实践指南
1. 项目概述:一个为现代应用而生的开源监控告警平台如果你和我一样,在运维或开发岗位上摸爬滚打了几年,一定经历过被监控告警系统折磨的时光。要么是传统的方案太重,部署一套下来服务器资源先吃紧一半;要么是云厂商的托…...
新手入门 Taotoken 从注册到获取第一个 API Key 全指南
新手入门 Taotoken 从注册到获取第一个 API Key 全指南 1. 注册 Taotoken 账号 访问 Taotoken 官方网站完成账号注册流程。在浏览器地址栏输入 https://taotoken.net 进入首页,点击右上角的「注册」按钮。填写邮箱地址、设置密码并完成手机号验证后,系…...
为AI助手集成BigDataCloud MCP Server:实现IP定位与数据验证
1. 项目概述:当AI助手学会“看地图”与“查户口” 如果你经常和Claude、Cursor或者GitHub Copilot这类AI助手打交道,有没有想过让它们变得更“接地气”?比如,你正在写一个用户注册表单,想让AI帮你验证用户输入的手机号…...
