数据结构——动手实战双向链表

数据结构实战:用双向循环链表实现高精度PI计算 本文详细介绍了如何使用双向循环链表实现高精度PI计算,通过泰勒展开与链表运算的结合,动态扩展存储空间并高效处理进位操作。文章包含完整的C语言代码实现,逐行解析核心算法,并提供了性能优化与实用技巧,帮助开发者掌握数据结构在高精度计算中的应用。 阅读详情

本文始发于个人公众号:TechFlow,原创不易,求个关注


在之前介绍SkipList的文章当中,有一些同学反馈说由于对链表缺少认知以及了解,所以直接啃算法有些过于困难。加上之前的文章当中介绍过了栈,所以这次继续线性表这个话题,我们来一起讨论一下链表。

链表是很多数据结构的基础,它的最大特点是支持快速的删除和插入,因此在很多数据频繁变动的场景下使用广泛。而且链表的可拓展性较强,所以它的应用非常广泛,相关的拓展和改进版本也很多。今天我们和大家介绍的是双端链表,也称为双向链表,它是寻常单向链表的改进版本,也是会经常使用的链表。


单向链表


链表(Linked list)是一种常见的基础数据结构,是一种线性表,但是并不会按线性的顺序存储数据,而是在每一个节点里存到下一个节点的指针(Pointer)。以上是维基百科当中的定义,我们可以明白两点,首先链表由多个节点组成的,每个节点存储了下一个节点的位置。其次,链表的各个节点不是顺序存储的。

我们看一下下图可以加深一下了解:

上图当中展示是单向链表,单向的意思是说每个节点只有一个指向后继节点的指针,也就是说链表只有一个遍历方向,因此称为是单向链表。


链表的增删


初学者在学习链表时可能会头疼它的使用,相比于数组的直接访问,链表需要通过移动指针来遍历节点修改节点的内容来完成增删,因此不如数组直观。我一直想要找到一个很好的例子来比喻链表运行的机制,直到有一次看谍战片,我发现间谍联络的系统就是以链表形式工作的。所以我决定以间谍系统来举例,介绍一下链表的工作原理。

假设你是民国时期军统的头目,你负责一个谍报链路。为了安全,你和你的手下们是单向联系的。也就是说只能你联系你的一个手下,由这个手下再去联络其他人,最终把暗号交到目标的手上。假设你的代号是A,你的手下是B,B的手下是C,C来执行任务。这个机制一直运转很好,直到有一天,B因为神秘原因转移了地点,导致A和B联络的时间变长,为了解决这个问题,你决定新增一个人手D专门负责A和B之间的联络,加快联络速度。你应该怎么办呢?

由于身份限制,以及安全原因,你是不知道B的具体信息的。你只能将消息给到A,让A去联络新人D,并且告诉他B的联络方法。还有一点需要注意,A必须等到D成功找到了B之后再切断和B的联系。否则一旦D出现什么意外,这整条链路就断了。

我们把整个图画出来如下:

先让D和B取得联系,之后断开A和B的联系。但是有一个问题,由于A的后继只有一个,A如果指向D,那么B的位置就会丢失。所以我们需要先用一个临时变量cur存储下来B的位置,然后再让D指向这个cur。不过在Python当中,我们可以不用这么麻烦,利用Python的多变量赋值的方法,我们可以一行代码搞定。

代码如下:

def add(pos, new_node):
    new_node.next, pos.next = pos.next, new_node

假如过了一段时间,B又回到了原处,我们不需要D了,要删除这个节点该怎么办?很简单,直接让D将B的最新的联络方式给A就可以了。也就是说让A跨过D指向B即可。

来看图:

def delete(pos):
    pos.next = pos.next.next

双向链表


理解了单向链表,双向链表也就很简单了。双向的意思也很明显,每个节点除了记录后继节点的位置之外,还会记录源头节点的位置。有了双向指针之后不仅是获取来源节点方便而已,并且也可以很方便地对整个链表进行倒叙遍历和头部插入。

还记得我们之前Python专题当中介绍过的deque这个库吗?通过deque我们可以实现一个双向增删元素的队列,结合双向链表的定义,很容易发现deque其实就是保留了一部分api的双向链表。换句话说deque是基于双向链表实现的,就和栈是基于list实现的一样。

和单向链表相比,由于我们多了一个指针,理解和实现起来会更加容易,因为之前需要通过顺序关系以及临时变量完成的内容现在可以通过前向指针很轻易地实现了。

下面附上双向链表增删的代码:

def add(pos, new_node):
    pos.next, new_node.next = new_node, pos.next
    new_node.pre, new_node.next.pre = pos, new_node
    
    
def delete(pos):
    pos.next = pos.next.next
    # 这个时候pos.next已经更新
    pos.next.pre = pos

总结


双向链表本身并不复杂,也没有太多变化的花样,和之前介绍的SkipList相比要简单许多。我相信即使是初学者,只要自己动手实现一遍,也足够掌握。在我初学数据结构的时候,我非常抗拒使用链表,除了觉得寻址很麻烦,需要遍历整个链表耗时很大之外。另一个根本的原因是在C++当中链表的编写很麻烦,而且很容易有内存泄漏以及野指针问题。所以我当时尽可能地使用数组作为替代,并且甚至一度认为随着内存价格的降低,总有一天我们可以抛弃链表这个结构。

直到后来我学习了操作系统之后,我找到了一个必须使用链表的理由。因为在操作系统当中,内存并不是连续的,大部分内存都是分散的。当我们创建一个数组的时候,我们其实是在想操作系统申请一块连续的内存。我们申请的数组越大,这块内存也就越大。显然越大的内存越难申请到,因为内存大多被切分成了许多碎片,在资源不够的情况下,操作系统需要做大量的工作才能将碎片搜集起来。

而链表因为通过指针寻址,所以可以避免这个问题,链表当中的元素分散在内存各处,分摊了内存消耗的压力。这也是在操作系统领域当中,链表大量使用的原因。

最后,由于篇幅限制,我们没有放上全部的代码,想要获取完整双向链表代码的同学,可以关注我的公众号,回复“双向链表”获取。

今天的文章就到这里,如果觉得有所收获,请顺手点个关注或者转发吧,你们的举手之劳对我来说很重要。

C语言TAILQ双向有尾链表:原理、实现与实战应用 在C语言系统编程中,数据结构是构建高效软件的核心基础。链表作为一种经典的数据组织方式,能够灵活管理动态数据集合,但其传统实现常面临代码重复、边界条件易错等工程挑战。TAILQ(Tail Queue)作为一种侵入式双向有尾链表,通过精妙的二级指针设计,在O(1)时间复杂度内实现了安全的头部与尾部插入删除操作,显著提升了代码的可靠性与可维护性。其宏定义实现不仅消除了函数调用开销,更通过TAILQ_ENTRY和TAILQ_HEAD结构,为消息队列、连接池及任务调度器等场景提供了标准化组件。在Linux/Unix环 阅读详情

相关推荐

最新嵌入式学习DAY21 --- 数据结构(双向链表、栈、队列)_队列、栈,企业级项目实战讲解

本质上栈和队列都是线性表,只不过我们之前讲的顺序表和单链表对于哪里插入和哪里删除没有限制,头部、中间、尾部随便,无所谓,你需要哪种操作,都有,但是栈和队列就对顺序表和单链表的插入和删除操作进行了限制。当我学到一定基础,有自己的理解能力的时候,会去阅读一些前辈整理的书籍或者手写的笔记资料,这些笔记详细记载了他们对一些技术点的理解,这些理解是比较独到,可以学到不一样的思路。网安所有方向的技术点做的整理,形成各个领域的知识点汇总,它的用处就在于,你可以按照上面的知识点去找对应的学习资源,保证自己学得较为全面。

2401_84297455的博客 459

自己手写一个双向链表

看书再多不如亲自动手,下面就是我自己实现的一个双向链表 public class MyLinked { private Node head; private Node tail; public int len; private static class Node{ private Node prev; private ...

weixin_42606135的博客 865

提示词工程的几个参数

低 temperature + 低 top-p:输出最稳定、最可靠,适合需要准确性和一致性的场景。高 temperature + 高 top-p:输出最多样化、最富有创意,适合需要灵感和探索的场景。Max Length:您可以通过调整max length来控制大模型生成的 token 数。指定 Max Length 有助于防止大模型生成冗长或不相关的响应并控制成本。是一个字符串,可以阻止模型生成 token,指定是控制大模型响应长度和结构的另一种方法。例如,您可以通过添加 “11” 作为。

weixin_44212787的博客 1826

数据结构——双向链表编写

要求:使用封装工程代码完成双向链表的定义、创建、头插入、显示、替换、删除、销毁等功能。其定义为:1.创建2.插入3.遍历4.替换5.删除6.销毁。

weixin_48141383的博客 492

【mip-NeRF】使用一组二维图像渲染3D场景视频(mip-NeRF论文复现)

本文对mip-NeRF模型的pytorch实现进行了复现

Ryan2k的博客 1840

第零章 序 Chisel教程汇总

笔者目前西安交通大学微电子硕士在读,研究方向为数字集成电路设计与体系结构。鉴于目前RISC-V的火热程度,笔者参与了课题组的RISC-V项目,并了解、学习了颇受关注的新型硬件构建语言——Chisel,当前参与的神经网络加速器设计便是使用Chisel开发的。这是一门敏捷开发语言,据称要比传统的Verilog语言快很多。在亲身使用过后,笔者发现确实快了不少。这主要得益于Scala作为高级语言的简洁风格...

qq_34291505的博客 7万+

【数据结构篇】~双向链表(附源码)

【数据结构篇】~双向链表(附源码)

旺小仔的博客 556

服务器卡死排查实战:从现象分类到根因定位的完整手册

服务器卡死是运维中最令人头疼的故障之一,从游戏圈吐槽的“土豆服务器”到生产环境中的Linux系统无响应、rsync复制挂起,本质都是系统或进程失去正常响应能力。其原理通常涉及CPU、内存、磁盘IO或网络资源耗尽,以及内核和驱动异常。掌握系统化的排查方法,能够帮助工程师快速定位根因、缩短故障时间,避免盲目重启。本文从资源、层级、场景等多维度分类,结合top、dmesg、strace等工具,梳理一套从现象确认、指标收集、日志分析到进程定位的实战排查流程,并覆盖常见高频场景与预防治理建议。无论你是运维新手还是老手

weixin_33851177的博客 374

双向链表的简单实现

1.双向链表的结构注意:这里的“带头”和之前的文章中单链表和顺序表说的“头节点”是两个概念,实际前面的在单链表阶段称呼不严谨,但是更好的理解就直接称为单链表的头节点。带头链表里的头节点,实际为“哨兵位”,哨兵位节点不存储任何有效元素,只是站在这⾥“放哨的”“哨兵位”存在的意义:遍历循环链表避免死循环。

2303_80472275的博客 963

机器人轨迹规划——三次样条轨迹1(基于速度的求解方法)

三次样条轨迹是利用三次样条曲线来对给定的一系列点进行插值,从而得到一个光滑的轨迹。三次样条因为每段的多项式次数最高都是三阶的,所以可以得到2阶导数连续,也就是加速度连续的轨迹。假设给定了一系列的点q0q1q2qnq0​q1​q2​...qn​,以及每个点对应的参数t0t1t2tnt0​t1​t2​...tn​,计算一条三次样条曲线sts(t)st,该曲线通过给定的点q0q1q2qnq0​q1。

出门搔白首,若负平生志 1469

第十二章 供水管网水力模型软件介绍

本文介绍了国内外主要供水管网建模软件及其特点,重点分析了某高建模软件V14的创新功能。国际主流软件包括开源的EPANET、商业软件InfoWorksWS、WaterGEMS和MIKEURBAN,各具特色但存在应用局限。某高V14软件实现了管网GIS与模型一体化管理,采用一G多网、一网多模、一模多案、网模合一技术路线,支持全信息模型建立和动态数据更新。其创新点包括水量分配理念革新(精确/模糊匹配、三种用水模式)和首创Web服务功能。通过并发计算、GPU加速等技术优化性能,具有较高性价比。

weixin_45258875的博客 207

【数据结构】轻松创造一个自己的双向链表

双向链表 双向链表应用实例,双向链表的操作分析和实现 管理单向链表的缺点分析: 单向链表,查找的方向只能是一个方向,而双向链表可以向前或者向后查找。 向链表不能自我删除,需要靠辅助节点 ,而双向链表,则可以自我删除,所以前面我们单链表删除 时节点,总是找到 temp,temp 是待删除节点的前一个节点(认真体会). 分析了双向链表如何完成遍历,添加,修改和删除的思路 双向链表实现思路 分析 双向链表的遍历,添加,修改,删除的操作思路===》代码实现 遍历 方和 单链表一样,

doomwatcher的博客 581

高频仿真:高频天线设计_(4).天线参数分析与优化

通过上述步骤,我们展示了如何综合使用解析方法、数值优化方法和实验数据来设计和优化一个高效宽带天线。首先,我们通过解析方法确定了天线的初步长度;然后,使用遗传算法优化了天线的直径以最大化增益;最后,通过实验数据验证了天线的性能,包括驻波比和带宽。这一综合性方法可以有效地提升天线的设计和优化效果,确保其在实际应用中表现出色。

2401_87715305的博客 923

Claude SEO 的 seo-flow 技能深度解析:基于 FLOW 框架的证据驱动型 SEO 运营模型(Find → Leverage → Optimize → Win)

> 本文以仓库 [skills/seo-flow/SKILL.md](https://pre-link.gitcode.com/i/1086a9f2ae790727177ca7038d5b2e30) 为核心骨架,结合 [flow-framework.md](https://pre-link.gitcode.com/i/9e5aaded5dc9f7206efa777616c33042)、[提示词库索

gitblog_00012的博客 939

openclaw本地模型对接

使用是官方推荐的一种集成方式,它可以简化配置过程。但由于您的系统环境和之前复杂的配置历史,我为您梳理了一个核心流程,并增加了关键的“事后检查”步骤,以确保能从根本上解决问题。

suny8的专栏 2459

jdk-8u144-linux-x64.tar.gz.

该资源为搭建Linux系统Java环境用,资源的后缀为tar.gz格式的压缩包

数据结构与算法入门:复杂度分析、数组与链表核心精讲

数据结构与算法是计算机科学的核心基础,它研究如何高效地组织和处理数据。其核心原理在于通过抽象数据类型(ADT)定义数据与操作,并运用算法复杂度分析(特别是大O表示法)来量化不同解决方案的效率。掌握这些基础对于编写高性能、可扩展的代码具有关键的技术价值,是优化系统资源、应对海量数据处理挑战的基石。在应用场景上,无论是构建软件系统、进行数据库索引设计,还是应对技术面试,对基础数据结构如数组(提供快速随机访问)和链表(擅长动态插入删除)的深入理解与权衡都至关重要。本文以悉尼大学COMP9123课程第一周内容为线索

weixin_30847865的博客 388

计算机控制技术及应用

《计算机控制技术及应用》通过总结作者的科研成果和吸收国内外的先进理论、方法和技术,以计算机控制系统的网络化、开放化、智能化和集成化发展趋势为主线索,主要介绍计算机控制系统的基本概念和计算机控制系统的典型形式,计算机控制系统的数学描述与基本分析方法,以及数字PID控制器、最少拍控制系统、达林算法、状态变量设计法等设计方法及其各种改进算法。重点突出计算机控制系统输入输出通道接口的设计方法和典型控制系统的设计案例与实现方法等。

【C语言数据结构】手撕“带头双向循环链表”!彻底拿捏复杂链表操作

摘要:带头双向循环链表是链表结构的"完全体",通过引入不存储数据的哨兵节点,统一了所有插入删除操作。本文从C语言实现角度,详细解析了这种链表的三大优势:哨兵节点简化边界处理、双向指针自由遍历、循环结构无缝衔接。通过图解和代码演示,展示了如何优雅实现尾插/尾删、头插/头删等操作,所有操作都简化为两个节点之间的链接调整。这种设计消除了繁琐的NULL判断,使代码更加简洁可靠,是处理复杂链表问题的理想选择。

JasonRobert 1233

Devexpress16.2.6破解工具

Devexpress16.2.6破解工具Devexpress16.2.6破解工具Devexpress16.2.6破解工具Devexpress16.2.6破解工具

汉书HE8,好用的汇编代码编写工具

汉书HE8,好用的汇编代码编写工具早期编写汇编代码所用功能强大

mysql存储结构双向链表_数据结构与算法系列五(双向链表)

1.引子1.1.为什么要学习数据结构与算法?有人说,数据结构与算法,计算机网络,与操作系统都一样,脱离日常开发,除了面试这辈子可能都用不到呀!有人说,我是做业务开发的,只要熟练API,熟练框架,熟练各种中间件,写的代码不也能“飞”起来吗?于是问题来了:为什么还要学习数据结构与算法呢?#理由一:面试的时候,千万不要被数据结构与算法拖了后腿#理由二:你真的愿意做一辈子CRUD Boy吗#理由三:不想写...

weixin_39640911的博客 380

循环双向链表详解:从原理到实战,解锁高效数据结构设计

链表作为基础的数据结构,通过指针链接实现动态存储,克服了数组固定大小的局限。其核心原理在于节点间通过指针建立逻辑顺序,而非物理连续。双向链表在此基础上引入前驱指针,支持双向遍历,提升了操作灵活性。循环结构则进一步消除了端点概念,使遍历无界,逻辑统一。这一设计在需要快速两端操作和循环访问的场景下极具技术价值,例如实现双端队列、LRU缓存淘汰算法、音乐播放列表循环播放等。本文以C语言为例,深入剖析循环双向链表的节点设计、核心操作(如插入、删除)的指针变化,并探讨了哨兵节点如何简化边界处理。通过结合**LRU缓存

weixin_42116058的博客 324

二叉搜索树转双向链表:中序遍历的终极实现与实战指南

二叉搜索树转双向链表是算法面试中的经典问题,掌握这一技能不仅能加深对树结构的理解,更能提升对中序遍历应用的实战能力。本文将通过《剑指 Offer》的经典案例,带你系统掌握这一问题的完整解决方案。 ## 为什么选择中序遍历? 二叉搜索树(BST)的独特性质在于**中序遍历结果为递增序列**,这为我们构建排序双向链表提供了天然优势。转换过程需要满足三个核心要求: - **排序性**:链表节点需按

gitblog_00324的博客 831

FreeRTOS内核实现:双向链表数据结构的设计原理与实战应用

在嵌入式实时操作系统(RTOS)中,数据结构是系统高效运行的基石。双向链表作为一种基础数据结构,通过节点间的双向指针链接,实现了高效的插入、删除和遍历操作,其时间复杂度可优化至O(1)或O(n)。这种特性对于资源受限且对实时性要求极高的嵌入式系统具有重要技术价值,它能够有效管理内核对象,支撑任务调度、同步通信等核心功能。FreeRTOS作为一款广泛应用的RTOS,其内核正是基于精心设计的双向链表来组织任务就绪列表、阻塞列表等关键数据结构。本文将以FreeRTOS为例,深入剖析其环形双向链表与MiniList

diandingyin9417的博客 334

循环双向链表:从基础原理到LRU缓存实战详解

链表作为基础数据结构,通过节点间的指针链接实现动态存储,克服了数组固定大小的限制。其核心原理在于每个节点包含数据域与指针域,通过指针维护元素间的逻辑顺序。双向链表在此基础上引入前驱指针,支持双向遍历,提升了操作灵活性。循环双向链表进一步将首尾相连形成闭环,消除了边界条件,使操作逻辑高度统一,在工程实践中展现出独特的技术价值。这种结构特别适用于需要环形访问或高效首尾操作的场景,例如实现LRU缓存淘汰算法。LRU算法通过哈希表与循环双向链表的结合,能以O(1)时间复杂度完成数据访问、插入与淘汰,是缓存系统、任务

weixin_29218509的博客 205

数据结构与算法之链表学习方法攻略

来源商业新知网,原标题:题型篇 | 数据结构与算法之链表系列 写在前边 如果你和小鹿一样,刚开始对链表的操作代码实现很懵的话,不妨按照小鹿经过一个月的时间对链表相关操作以及题型的整理总结,由浅入深进行适当的练习,我相信,当你真正的练习完这些题目,不但会让你放下对链表心理上的困惑,而且对你学习其他数据结构有很大的信心和帮助! 由于文章篇幅共计 8000 字,公众号为缩减版本,完...

200

题型篇 | 数据结构与算法之链表系列

来源:不甘平凡的码农 链接:https://mp.weixin.qq.com/s/-CGWfIXBqpVoaRHpm1bgQw写在前边如果你和小鹿一样,刚开始对链表的...

python爬虫人工智能大数据 222

Java实战:手把手教你用双向链表与哈希表构建高效LRU缓存

本文手把手教你使用Java实现高效的LRU缓存。通过结合双向链表维护访问顺序和哈希表实现O(1)快速访问,详细解析了核心设计思想、代码实现步骤、复杂度分析及常见避坑指南。掌握这一经典数据结构组合,是提升算法能力和应对技术面试的关键。

weixin_29223603的博客 182

算法与数据结构 - 链表详解

文章目录前言引言业务实现1. 通过数组保存热搜信息一、链表简介1.1 什么是链表1.2 图话链表二、手撕链表2.1 节点编写2.2 链表基础结构2.3 链表的插入2.3.1 头插法2.3.2 尾插法三、链表拓展2.1 双向链表2.2 环状链表2.3 跳表 前言 点赞再看,养成习惯! 引言 韩梅梅是个喜欢网上冲浪的八卦达人 今天韩梅梅在某博刷热搜的时候发现如下排序: 某啤酒品牌起诉吴某某 热剧且试夏天剧照 某学者喊话某网站要清醒 西瓜价格跌破5毛 某市新房成交量首次低于二手房 正当韩梅梅觉得今天的热

xiaoai1994的博客 334
上一篇: 机器学习基础——详解自然语言处理之tf-idf
下一篇: 看完这篇让你高数不挂科之——泰勒公式
TechFlow
博客等级 码龄7年 347粉丝 276原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值