深入理解数据结构:链表
文章目录
- 🌰导语
- 🌰链表的定义及基本结构
- 🌰单链表
- 🥕单链表特点
- 🌰双向链表
- 🥕双链表特点
- 🌰循环链表
- 🥕循环链表特点
- 🌰链表的操作
- 🍆链表的插入
- 🫘链头插入
- 🫘链间插入
- 🍆链表的删除
- 🫘链头删除
- 🫘链间删除
- 🍆链表的查询
- 🌰链表的应用场景
- 🌰链表与数组的比较
- 🌳存储方式
- 🌳插入和删除操作
- 🌳访问效率
- 🌳空间效率
- 🌰结语
🌰导语
链表是一种常用的数据结构,通过节点之间的指针连接而成,具有动态性和高效的插入删除操作。本文将深入介绍链表的定义、类型、操作以及应用场景,帮助读者全面理解链表的原理和使用。
🌰链表的定义及基本结构
链表是一种由节点组成的数据结构,每个节点包含存储数据的部分以及指向下一个节点的指针。通过节点之间的指针连接,形成了链表的结构。链表可以分为单链表、双向链表和循环链表等不同类型,它们各自具有特定的特点和应用场景。
- 数据元素:节点存储的实际数据。数据可以是任意类型,例如整数、字符、字符串、对象等。
- 指针(或引用):指向下一个节点的指针。它存储了下一个节点在内存中的地址,通过这个指针可以找到链表的下一个节点。

🌰单链表
单链表是最简单的链表类型,每个节点包含一个数据元素和一个指向下一个节点的指针。单链表的尾节点指针为空,表示链表的结束。单链表的插入、删除操作相对简单高效,但随机访问的效率较低。
结构图:

🥕单链表特点
-
非连续存储:单链表中的节点在内存中可以是任意位置,不要求连续存储。每个节点通过指针指向下一个节点,从而将它们连在一起。
-
动态性:相较于数组,单链表的长度可以动态地增减,不需要预先分配内存空间。这使得单链表在需要频繁插入和删除节点的场景中更加灵活。
-
搜索效率相对较低:由于单链表的节点只能通过指针一个一个地遍历,因此搜索某个特定的节点需要遍历整个链表,时间复杂度为O(n),其中n是链表的长度。相比之下,数组可以使用索引直接访问元素,搜索效率更高。
-
内存空间的额外开销:单链表中的每个节点除了存储数据外,还需要存储下一个节点的指针,这导致了额外的内存开销。
-
插入和删除效率较高:相对于数组,单链表在插入和删除节点时效率较高。插入一个节点只需要改变相邻节点的指针,而删除一个节点只需要改变前一个节点的指针指向下一个节点,不需要移动其他元素。
-
灵活性:单链表可以方便地进行节点的插入和删除操作,可以根据实际需要进行自由调整。
🌰双向链表
双向链表在单链表的基础上增加了一个指向前一个节点的指针,使得节点既能够往后访问,也能够往前访问。这样的设计使得插入和删除操作更加灵活便捷,但相应地占用了更多的存储空间。
结构图:

🥕双链表特点
-
支持双向遍历:相对于单向链表,双向链表支持双向遍历,可以从前往后、从后往前遍历链表。
-
插入、删除节点效率更高:相较于单向链表,双向链表在插入和删除某个节点时,只需要改变相邻两个节点的指向,效率更高,尤其是在删除链表中某个元素时更加便捷。
-
需要更多的内存空间:相比单向链表,双向链表需要更多的内存空间来存储额外的指针,增加了额外的空间开销。
-
内存存储不连续:相对于数组,链表中节点的内存存储位置不连续,需要使用指针进行串联,这可以更加灵活地进行插入、删除和移动节点的操作。
-
操作复杂度较高:插入、删除、移动等操作,需要修改前后节点的指针信息,操作比较繁琐。
🌰循环链表
循环链表是一种特殊的链表类型,链表的尾节点指针指向头节点,形成一个循环的结构。循环链表可以通过尾节点快速访问链表的头部,常用于某些特定场景,如循环队列的实现。
结构图:

🥕循环链表特点
-
首位相连:循环链表的最后一个节点指向链表的第一个节点,使得链表成为一个环形结构。这样,链表的结束节点与开始节点相连,可以实现无限循环,更加灵活和方便。
-
操作始终成立:由于循环链表始终是一个环形的结构,因此操作(例如插入、删除、查找等)始终处于链表中,这也保证了操作始终能够完成。
-
遍历循环:与单向链表相比,在遍历时需要注意指针不要陷入死循环。否则会导致遍历永远无法结束。
-
内存空间的额外开销:与单向链表相比,循环链表需要多一个指向头部节点的指针,增加了额外的空间开销。
-
插入和删除效率较高:相对于数组,循环链表在插入和删除节点时效率比较高。插入一个节点时只需要修改相邻节点的指针即可,而删除一个节点时只需要改变前一个节点的指针指向下一个节点即可。
🌰链表的操作
链表的常见操作包括插入、删除和查询。链表的插入操作可以在指定位置或者链表尾部插入一个节点,只需要调整相应节点的指针即可。链表的删除操作类似,只需要改变相应节点的指针即可删除指定节点。链表的访问操作需从头节点开始遍历,直到找到目标节点或遍历到链表尾部。同时,需要注意链表操作时需要处理特殊情况,如链表为空或插入删除节点是头节点或尾节点的情况。
🍆链表的插入
🫘链头插入

代码示例
class Node:def __init__(self, data):self.data = dataself.next = Nonedef insert_at_head(head, value):# 创建新节点new_node = Node(value)# 将新节点的下一个指针指向当前头节点new_node.next = head# 更新头节点为新节点head = new_nodereturn headdef display_linked_list(head):current = headwhile current:print(current.data, "-> ", end="")current = current.nextprint("NULL")# 创建一个空链表
head = None# 在头部插入节点
head = insert_at_head(head, 3)
head = insert_at_head(head, 2)
head = insert_at_head(head, 1)# 打印链表
display_linked_list(head)
打印结果:
1 -> 2 -> 3 -> NULL
🫘链间插入

程序示例:
class Node:def __init__(self, data):self.data = dataself.next = Nonedef insert_in_between(node, value):if not node:return Nonenew_node = Node(value)new_node.next = node.nextnode.next = new_nodedef display_linked_list(head):current = headwhile current:print(current.data, "-> ", end="")current = current.nextprint("NULL")# 创建一个链表
head = Node(1)
node2 = Node(2)
node3 = Node(3)
node4 = Node(4)head.next = node2
node2.next = node3
node3.next = node4# 在节点2和节点3之间插入节点
insert_in_between(node2, 2.5)# 打印链表
display_linked_list(head)
打印结果:
1 -> 2 -> 2.5 -> 3 -> 4 -> NULL
🍆链表的删除
🫘链头删除

代码示例:
class Node:def __init__(self, data):self.data = dataself.next = Nonedef delete_at_head(head):if not head:return Nonenew_head = head.nexthead.next = Nonereturn new_headdef display_linked_list(head):current = headwhile current:print(current.data, "-> ", end="")current = current.nextprint("NULL")# 创建一个链表
head = Node(1)
node2 = Node(2)
node3 = Node(3)head.next = node2
node2.next = node3# 删除头节点
head = delete_at_head(head)# 打印链表
display_linked_list(head)
运行结果:
2 -> 3 -> NULL
🫘链间删除

示例代码:
class Node:def __init__(self, data):self.data = dataself.next = Nonedef delete_in_between(head, value):if not head:return Nonecurrent = headprev = Nonewhile current and current.data != value:prev = currentcurrent = current.nextif not current:return headif not prev:head = head.nextelse:prev.next = current.nextcurrent.next = Nonereturn headdef display_linked_list(head):current = headwhile current:print(current.data, "-> ", end="")current = current.nextprint("NULL")# 创建一个链表
head = Node(1)
node2 = Node(2)
node3 = Node(3)
node4 = Node(4)head.next = node2
node2.next = node3
node3.next = node4# 删除节点3
head = delete_in_between(head, 3)# 打印链表
display_linked_list(head)
运行结果:
1 -> 2 -> 4 -> NULL
🍆链表的查询
代码示例:
class Node:def __init__(self, data):self.data = dataself.next = Nonedef search_node(head, value):current = headwhile current:if current.data == value:return Truecurrent = current.nextreturn False# 创建一个链表
head = Node(1)
node2 = Node(2)
node3 = Node(3)
node4 = Node(4)head.next = node2
node2.next = node3
node3.next = node4# 查询节点3
found = search_node(head, 3)if found:print("节点找到")
else:print("节点未找到")
🌰链表的应用场景
链表在实际应用中有许多重要的应用场景。其中,LRU Cache(最近最少使用缓存)获取和替换缓存中的数据时,可以使用链表实现高效的插入和删除操作。链表反转可以使用链表的插入和删除操作实现,将原链表的节点反向连接,获得反转后的链表。另外,链表排序也是链表的经典应用之一,通过比较和交换链表节点的值,实现链表的排序操作。
🌰链表与数组的比较
链表和数组是两种不同的数据结构,它们在以下方面有所区别:
🌳存储方式
数组是一块连续的内存空间,可以通过下标访问任意位置的元素;而链表是由节点组成,每个节点包含数据和指向下一个节点的指针。
🌳插入和删除操作
在数组中,插入和删除操作可能需要移动其他元素以保持连续性,时间复杂度为 O(n);而链表中插入和删除操作只需要更新节点的指针,时间复杂度通常为 O(1)。
🌳访问效率
在数组中,通过下标可以直接访问元素,时间复杂度为 O(1);而链表需要从头节点开始遍历,平均情况下需要 O(n) 时间。
🌳空间效率
对于相同的元素数量,链表通常需要更多的空间,因为每个节点都需要额外的指针来指向下一个节点;而数组只需要连续的内存空间。
🌰结语
链表作为一种重要的数据结构,在算法和数据结构中扮演着重要的角色。通过深入理解链表的原理和操作,我们可以更好地应用它来解决实际问题。
链表的灵活性使得它在许多领域有着广泛的应用。其中,LRU Cache就是一个很好的例子。LRU Cache是一种常见的缓存策略,它会保留最近最常使用的数据,而淘汰最近最少使用的数据。这可以通过链表来实现。链表的头部表示最近最常使用的数据,而尾部表示最近最少使用的数据。当有新数据加入时,我们可以将其添加到链表的头部。而当缓存满时,我们可以淘汰链表尾部的数据。这就是链表的高效插入和删除操作在实际应用中的体现。
链表的反转也是一个重要的应用场景。通过链表的插入和删除操作,我们可以将原始链表的节点逐个取出,并依次插入到新链表的头部,从而实现链表的反转。这个过程只需要简单地修改指针的指向,时间复杂度为O(n),其中n为链表的长度。链表的反转在实际开发中经常遇到,比如反转字符串、反转链表以及翻转二叉树等。
此外,链表的排序也是另一个常见的应用场景。链表的排序可以采用排序算法中的一些经典方法,比如插入排序、归并排序和快速排序等。其中,归并排序在链表中有着很好的适用性。归并排序是一种分治的排序算法,通过递归地将链表划分为更小的子链表,然后将它们按顺序合并。这样,在每一层递归中,我们可以通过比较两个已排序的子链表的头部节点,选择较小的节点,进行合并。通过这种方式,我们可以实现链表的排序操作。
在选择使用链表还是数组时,我们需要根据实际情况权衡它们之间的优缺点。链表的插入和删除操作相对高效,而数组的随机访问效率较高。因此,在需要频繁插入和删除的场景下,链表是一个更好的选择。而在需要快速根据索引查找元素的场景下,数组更具优势。此外,在空间方面,链表需要额外的指针来连接节点,而数组则需要连续的存储空间。因此,如果内存空间有限,我们可能需要考虑使用链表。
总结起来,链表是一种重要的数据结构,具有许多广泛应用的场景。通过深入理解链表的原理和操作,我们可以更好地应用它来解决实际问题。无论是LRU Cache的实现、链表的反转还是链表的排序,都需要我们熟练掌握链表的特性和操作方法。
🏫博客主页:魔王-T
🏯系列专栏:结构算法
🥝大鹏一日同风起 扶摇直上九万里
❤️感谢大家点赞👍收藏⭐评论✍️
相关文章:
深入理解数据结构:链表
文章目录 🌰导语🌰链表的定义及基本结构🌰单链表🥕单链表特点 🌰双向链表🥕双链表特点 🌰循环链表🥕循环链表特点 🌰链表的操作🍆链表的插入🫘链头…...
7:kotlin 数组 (Arrays)
数组是一种数据结构,它保存固定数量的相同类型或其子类型的值。kotlin中最常见的数组类型是对象类型数组,数组由array类表示。 什么时候使用 当你在kotlin中有特殊的底层需求需要满足时,可以使用数组。例如,如果你有超出常规应用…...
mysql 变量和配置详解
MySQL 中还有一些特殊的全局变量,如 log_bin、tmpdir、version、datadir,在 MySQL 服务实例运行期间它们的值不能动态修改,也就是不能使用 SET 命令进行重新设置,这种变量称为静态变量。数据库管理员可以使用前面提到的修改源代码…...
算法基础之合并集合
合并集合 核心思想:并查集: 1.将两个集合合并2.询问两个元素是否在一个集合当中 基本原理:每个集合用一棵树表示 树根的编号就是整个集合的编号 每个节点存储其父节点,p[x]表示x的父节点 #include<iostream>using namespace std;const int N100010;int p[N];…...
在使用微信或者支付宝支付的时候,为什么微信支付或者支付宝支付的异步通知商户支付结果要进行验签?
在使用微信支付或支付宝支付等第三方支付平台时,异步通知是一种常见的机制,用于通知商户支付结果或交易状态的变化。验签(Signature Verification)是为了确保异步通知的安全性和完整性而进行的重要步骤。以下是为什么要进行验签的…...
带你用uniapp从零开发一个仿小米商场_5. 公共样式编写,
先前引入了公共样式,但公共样式文件里面还没有编写内容 在这里我将一一讲解公共样式内应该有什么样式,和为什么 首先给page添加高度和背景色,当然这个背景色可以在app.vue内添加 page{/* 设置page高,让每个页面的最小高度为整个视窗的高 */min-height: 100vh; /* 统一字体大小…...
2019年全国硕士研究生入学统一考试管理类专业学位联考英语(二)试题
文章目录 2019年考研英语二真题SectionⅠ Use of EnglishSection II Reading ComprehensionText 121——细节信息题22——细节信息题23——细节信息题24——细节信息题25——词义题 Text 226——细节信息题27——细节信息题28——细节信息题29——细节信息题30——态度题 Text …...
基于C#实现Kruskal算法
这篇我们看看第二种生成树的 Kruskal 算法,这个算法的魅力在于我们可以打一下算法和数据结构的组合拳,很有意思的。 一、思想 若存在 M{0,1,2,3,4,5}这样 6 个节点,我们知道 Prim 算法构建生成树是从”顶点”这个角度来思考的,然…...
卷积神经网络经典backbone
特征提取是数据分析和机器学习中的基本概念,是将原始数据转换为更适合分析或建模的格式过程中的关键步骤。特征,也称为变量或属性,是我们用来进行预测、对对象进行分类或从数据中获取见解的数据点的特定特征或属性。 1.AlexNet paper&#…...
【2023 年终盘点】今年用的最多的 10 款浏览器插件
分享顺哥今年用的最多的 10 款浏览器插件。 排名不分先后,涉及各个方面的应用。 大家有好用的插件也欢迎在评论区留言分享! 视频 YouTube:https://youtu.be/ZpTydUSBwCA 顺哥博客 浏览器扩展篇 注意: 1、以下介绍的均为在 Google Chrome 浏览器适用的小插件,部分插件…...
GWAS:plink进行meta分析
之前教程提到过Metal是可以做Meta分析,除了Metal,PLINK也可以进行Meta分析。 命令如下所示: plink --meta-analysis gwas1.plink gwas2.plink gwas3.plink logscale qt --meta-analysis-snp-field SNP --meta-analysis-chr-field CHR --me…...
web:[WUSTCTF2020]朴实无华
题目 点开页面显示如下 页面显示了一行报错:Cannot modify header information - headers already sent by (output started at /var/www/html/index.php:3) in /var/www/html/index.php on line 4 意思为不能修改报头信息-报头已经发送(输出开始于/var/www/html/i…...
云原生安全工具汇总(docker、k8s、Kubernetes、Git仓库)
目录 Metarget:云原生靶机环境 CDK:容器环境定制的渗透测试工具 container-escape-check:容器逃逸检测...
基于51单片机超声波测距汽车避障系统
**单片机设计介绍, 基于51单片机超声波测距汽车避障系统 文章目录 一 概要二、功能设计设计思路 三、 软件设计原理图 五、 程序六、 文章目录 一 概要 基于51单片机的超声波测距汽车避障系统是一种用于帮助汽车避免碰撞和发生事故的设备,以下是一个基本…...
git的使用:本地git下载、sshkey的添加、github仓库创建及文件上传
一、github创建账号 即github注册账号,登录github官网,根据提示注册即可 github官网 二、git客户端下载安装 已有很多git下载安装的博文了,在此就不赘述 三、sshkey的生成与添加 1、sshkey的生成以及查看 // sshkey的生成命令ÿ…...
增量有余、后劲不足,星途汽车10月份销量环比下降3.9%
撰稿|行星 来源|贝多财经 近日,奇瑞集团发布了10月销量月报。报告显示,奇瑞集团于2023年10月销售汽车20.03万辆,同比增长50.8%,单月销量首次突破20万辆;2023年前10个月的累计销量为145.36辆,同比增长41.6…...
只考数据结构,计算机评级C+,成都信息工程大学考情分析
成都信息工程大学(C) 考研难度(☆☆) 内容:23考情概况(拟录取和复试分析)、院校概况、24专业目录、23复试详情、各专业考情分析、各科目考情分析。 正文1715字,预计阅读:3分钟 2023考情概况 …...
Screen操作
detach:detach是指将当前运行的Screen会话从终端分离(detach),使其在后台继续运行而不受当前终端窗口的影响。这样,你可以在一个终端窗口中启动一个Screen会话,然后在需要的时候将其分离,使其在…...
js基础知识
1. beforeCreate 初始化界面前 : 在当前阶段data、methods、computed以及watch上的数据和方法都不能被访问。 2. created 初始化界面后 : 在实例创建完成后发生,当前阶段已经完成了数据观测,也就是可以使用数据,更改数据,在这里更…...
Vue常见的实现tab切换的两种方法
目录 方法一:事件绑定属性绑定 效果图 完整代码 方法二:属性绑定 动态组件 component标签 效果图 完整代码 方法一:事件绑定属性绑定 效果图 完整代码 <!DOCTYPE html> <html lang"en"> <head><meta c…...
基于算法竞赛的c++编程(28)结构体的进阶应用
结构体的嵌套与复杂数据组织 在C中,结构体可以嵌套使用,形成更复杂的数据结构。例如,可以通过嵌套结构体描述多层级数据关系: struct Address {string city;string street;int zipCode; };struct Employee {string name;int id;…...
ssc377d修改flash分区大小
1、flash的分区默认分配16M、 / # df -h Filesystem Size Used Available Use% Mounted on /dev/root 1.9M 1.9M 0 100% / /dev/mtdblock4 3.0M...
全球首个30米分辨率湿地数据集(2000—2022)
数据简介 今天我们分享的数据是全球30米分辨率湿地数据集,包含8种湿地亚类,该数据以0.5X0.5的瓦片存储,我们整理了所有属于中国的瓦片名称与其对应省份,方便大家研究使用。 该数据集作为全球首个30米分辨率、覆盖2000–2022年时间…...
React19源码系列之 事件插件系统
事件类别 事件类型 定义 文档 Event Event 接口表示在 EventTarget 上出现的事件。 Event - Web API | MDN UIEvent UIEvent 接口表示简单的用户界面事件。 UIEvent - Web API | MDN KeyboardEvent KeyboardEvent 对象描述了用户与键盘的交互。 KeyboardEvent - Web…...
Android第十三次面试总结(四大 组件基础)
Activity生命周期和四大启动模式详解 一、Activity 生命周期 Activity 的生命周期由一系列回调方法组成,用于管理其创建、可见性、焦点和销毁过程。以下是核心方法及其调用时机: onCreate() 调用时机:Activity 首次创建时调用。…...
sipsak:SIP瑞士军刀!全参数详细教程!Kali Linux教程!
简介 sipsak 是一个面向会话初始协议 (SIP) 应用程序开发人员和管理员的小型命令行工具。它可以用于对 SIP 应用程序和设备进行一些简单的测试。 sipsak 是一款 SIP 压力和诊断实用程序。它通过 sip-uri 向服务器发送 SIP 请求,并检查收到的响应。它以以下模式之一…...
C/C++ 中附加包含目录、附加库目录与附加依赖项详解
在 C/C 编程的编译和链接过程中,附加包含目录、附加库目录和附加依赖项是三个至关重要的设置,它们相互配合,确保程序能够正确引用外部资源并顺利构建。虽然在学习过程中,这些概念容易让人混淆,但深入理解它们的作用和联…...
django blank 与 null的区别
1.blank blank控制表单验证时是否允许字段为空 2.null null控制数据库层面是否为空 但是,要注意以下几点: Django的表单验证与null无关:null参数控制的是数据库层面字段是否可以为NULL,而blank参数控制的是Django表单验证时字…...
破解路内监管盲区:免布线低位视频桩重塑停车管理新标准
城市路内停车管理常因行道树遮挡、高位设备盲区等问题,导致车牌识别率低、逃费率高,传统模式在复杂路段束手无策。免布线低位视频桩凭借超低视角部署与智能算法,正成为破局关键。该设备安装于车位侧方0.5-0.7米高度,直接规避树枝遮…...
Python竞赛环境搭建全攻略
Python环境搭建竞赛技术文章大纲 竞赛背景与意义 竞赛的目的与价值Python在竞赛中的应用场景环境搭建对竞赛效率的影响 竞赛环境需求分析 常见竞赛类型(算法、数据分析、机器学习等)不同竞赛对Python版本及库的要求硬件与操作系统的兼容性问题 Pyth…...
