双向链表增删改查的模拟实现

本章目标

0.双向链表的基本结构
1.双向链表的初始化
2.头插尾插
3.头删尾删
4.查找与打印
5.在指定位置之前插入数据/在指定位置之后插入数据
6.在指定位置之前删除数据/在指定位置之后删除数据
7.销毁链表

0.双向链表的基本结构

本章所实现的双向链表是双向循环带头链表,是stl中list容器的结构一致.
在这里插入图片描述
它有一个真正意义上的头结点,但该结点并不存储数据,我们一般管它叫做哨兵位.
它与单链表不同的是它的结点中有两个指向结点的指针,分别是前一个结点和下一个结点的指针.

typedef int listdatatype;struct listNode
{listdatatype data;struct listNode* prev;struct listNode* next;
};
typedef struct listNode listNode;

我们把数据类型和链表结点进行typedef方便后面我们进行实现该链表的函数.

1.双向链表的初始化

与单链表不同的是,每当我们创建链表的时候,需要创建一个新的哨兵位,然后才能将数据进行插入,删除操作.既然要创建一个新的结点,我们就需要将该逻辑进行分装,方便后续使用.

listNode* buyNode(listdatatype x)
{listNode* newnode = (listNode*)malloc(sizeof(listNode));if (!newnode){perror("malloc fail");exit(1);}newnode->data = x;newnode->next = newnode->prev = newnode;return newnode;
}

对于头节点的创建我们有两种方法,第一个中是在函数体内实现,另一种是传过来一个指针进行操作,如果时第二种方法,我们就需要传址,需要二级指针,如果传一级指针相当于传值操作,当函数结束的时候,函数栈帧会自动销毁,那么外面的指针就成为野指针了.

listNode* listinit()
{listNode* newnode = buyNode(-1);return newnode;}void listintalpha(listNode **pphead)
{*pphead = buyNode(-1);
}

因为该结点时头节点,并不存储数据,所以在创建结点时给的参数可以随便给.

2.头插尾插

2.1头插

在这里插入图片描述
对于头插来说,我们需要操作三个部分,哨兵位,哨兵位的下一个结点,newnode.
我们先讨论newndoe,要让它的prev指向哨兵位,next指向哨兵位的下一个结点,该操作时不会对链表产生影响的.我可以随意他们的顺序.
接着让哨兵位的下一个结点的prev指向newnode,然后再让哨兵位的next指向newnode.
该操作是有顺序要求的,如果先让哨兵位的next指向newnode,会找不到后面的链表,当然我们也可以提前保存一份,这样就可以随意操作了.

void listpushfront(listNode* phead, listdatatype x)
{assert(phead);listNode* newnode = buyNode(x);newnode->prev = phead;newnode->next = phead->next;phead->next->prev = newnode;phead->next = newnode;
}

2.2.尾插

在这里插入图片描述
对于尾插来说,我们要操作的结点一共有三个,最后一个结点,哨兵位,newnode.
我们先操作对链表没有影响newnode,让它的prev的结点指向最后一个结点(头节点的prev结点)
让它的next结点指向哨兵位,然后让最后一个结点的下一个结点的指针指向newnode,让哨兵位的prev结点指向newnode.

void listpushback(listNode* phead,listdatatype x)
{assert(phead);listNode* newnode = buyNode(x);newnode->prev = phead->prev;newnode->next = phead;phead->prev->next = newnode;phead->prev = newnode;
}

3.头删尾删

3.0判断链表是否为空

当我们如果要进行删除操作的时候链表中一定是有元素的.我们可以将链表的判空封装成一个方法.
如果当前结点下一个结点仍然是当前结点就证明,该链表为空.

/判断是否非空
bool LTEmpty(listNode* phead)
{assert(phead);return phead->next == phead;
}

3.1头删

因为我们传过来的结点一定是哨兵位,我们删除的数据是它的下一个结点.为了防止丢失,我们可以提前将它保存为del.与它相关联的结点时哨兵位以及del的下一个结点,我们要让它的del的下一个结点的prev指向哨兵位,哨兵位的next指向del的下一个结点.然后释放del,置空.
该实现该函数的时候要注意判空.

void listpopfront(listNode* phead)
{assert(!LTEmpty(phead));listNode* del = phead->next;del->next->prev = phead;phead->next = del->next;free(del);del = NULL;
}

3.2尾删

对于尾删来说,我们要删除最后一个有数据的结点,与之关联的结点一共有两个,哨兵位以及它的前一个结点.我们要让它的前一个结点的next指向哨兵位,让哨兵位的prev指针指向要删除结点的前一个结点.然后释放该结点.我们可以提前保存该结点为del,方便操作.

void listpopback(listNode* phead)
{assert(!LTEmpty(phead));listNode* del = phead->prev;del->prev->next = phead;phead->prev = del->prev;free(del);del = NULL;
}

4.查找与打印

4.1查找

对于查找来说,它需要遍历整个链表,然后用当前值与该结点数据域中的值进行比对.如果找到就返回该结点,找不到就返回空.结束条件时当前结点等于哨兵位

listNode* listFind(listNode* phead, listdatatype x)
{listNode* pcur = phead->next;while (pcur != phead){if (pcur->data == x){return pcur;}pcur = pcur->next;}return NULL;
}

4.2打印

打印仍然时遍历链表,直到结束条件时当前结点等于哨兵位.

void print(listNode* phead)
{listNode* pcur = phead->next;while (pcur != phead){printf("%d->",pcur->data);pcur = pcur->next;}printf("\n");
}

5.在指定位置之前插入数据/在指定位置之后插入数据

5.1在指定位置之前插入数据

该函数实现涉及三个结点pos结点,pos结点的前一个结点.newnode.
我们让newnode的prev指向pos结点的前一个结点.它的next结点指向pos结点.
pos结点的前一个结点的next指针指向newnode,pos结点的prev指针指向newnode即可

void listinsertFront(listNode* pos, listdatatype x)
{assert(pos);listNode* newnode = buyNode(x);newnode->next = pos;newnode->prev = pos->prev;pos->prev->next = newnode;pos->prev = newnode;
}

5.2在指定位置之后插入数据

该方法实现涉及三个结点.pos结点.pos结点的下一个结点,newnode.
我们仍然newnode结点的prev和next分别指向pos结点和pos结点的下一个结点.
pos结点的下一个结点的prev指针指向newnode,pos结点的next指针指向newnode.

void listinsertBack(listNode* pos, listdatatype x)
{assert(pos);listNode* newnode = buyNode(x);newnode->next = pos->next;newnode->prev = pos;pos->next->prev = newnode;pos->next = newnode;
}

6.在指定位置之前删除数据/在指定位置之后删除数据

6.1在指定位置之前删除数据

我们先要将pos之前那个要删除的结点保存为del.
让del的前一个结点的next指针指向pos.
pos的prev指针指向del的前一个结点,然后释放del.
但再删除前仍然需要判空.

void listErasefront(listNode* pos)
{assert(!LTEmpty(pos));listNode* del = pos->prev;del->prev->next = pos;pos->prev = del->prev;free(del);del = NULL;
}

6.2在指定位置之后删除数据

我们要先将pos结点之后的那个要删除的结点保存为del,然后让del的下一个结点的prev指向pos.
pos的next指针指向del的下一个结点,然后释放del.

void listEraseback(listNode* pos)
{assert(!LTEmpty(pos));listNode* del = pos->next;del->next->prev = pos;pos->next = del->next;free(del);del = NULL;
}

7.销毁链表

与链表的初始化相对,它也有两种实现方式,但大体的思想都是循环销毁.

void destroyalpha(listNode** pphead)
{listNode* pcur = (*pphead)->next;while (pcur != (*pphead)){listNode* next = pcur->next;free(pcur);pcur = next;}free((*pphead));*pphead = NULL;
}void destroybata(listNode* phead)
{listNode* pcur = phead->next;while (pcur != phead){listNode* next = pcur->next;free(pcur);pcur = next;}free(phead);phead = NULL;}

但要注意第二种方式如果使用,我们需要再外面对结点置空,防止成为野指针,

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/bicheng/75783.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

实战交易策略 篇十四:江南神鹰捕捉热点和熊市生存交易策略

文章目录 系列文章捕捉热点是股市最大的掘金术市场温度不低于50是热点产生的必要条件题材的大小和新颖程度决定热点的持续时间和涨幅炒作热点的3个阶段捕捉热点的方法与步骤操作实战案例熊市生存术“熊市最好的做法是离开股市”的说法是一句空话熊市盈利模式:不轻言底部,超跌…

Linux错误(6)X64向量指令访问地址未对齐引起SIGSEGV

Linux错误(6)X64向量指令访问地址未对齐引起SIGSEGV Author: Once Day Date: 2025年4月4日 一位热衷于Linux学习和开发的菜鸟,试图谱写一场冒险之旅,也许终点只是一场白日梦… 漫漫长路,有人对你微笑过嘛… 全系列文章可参考专栏: Linux实…

解码 __iter__ 和 itertools.islice - 迭代的艺术

文章目录 前言一、`_iter__`:自定义迭代的钥匙1.1 什么是 __iter__?1.2 基本用法1.3 高级用法:独立迭代器二、itertools.islice:迭代切片的利器2.1 什么是 itertools.islice?2.2 基本用法2.3 处理无限序列2.4 实际应用三、`__iter__` 与 `islice` 的结合六、为什么需要它们…

使用VSCode编写C#程序

目录 一、环境搭建:构建高效开发基础1. 安装VSCode2. 配置.NET SDK3. 安装核心扩展 二、项目开发全流程1. 创建项目2. 代码编辑技巧3. 调试配置4. 高级调试技巧5. 编译与运行 三、常见问题解决指南1. 项目加载失败2. IntelliSense失效3. 代码格式化4. 典型编译错误&…

日本汽车规模性经济计划失败,日产三大品牌的合并合作共赢,还是绝地求生?本田与日产合并确认失败,将成为世界第三大汽车集团愿景失败

本田与日产(含三菱汽车)的合并计划最终因核心矛盾无法调和而宣告失败,这一事件揭示了传统车企在行业变革期的深层困境。以下从合并动机、失败原因、本质判断及未来影响等方面综合分析: 一、合并的初衷:生存压力主导的被动策略 市场危机与财务困境 中国市场溃败:日系品牌在…

AutoCAD2026中文版下载安装教程

AutoCAD是一款由Autodesk公司开发的计算机辅助设计软件,被广泛应用于建筑设计、机械设计、电气设计、土木工程、装饰装潢等多个领域。AutoCAD2026中文版在原有的基础上进行了多项改进和优化,为用户提供了更为高效、便捷的绘图和设计体验。这里我给大家分…

Latex语法入门之数学公式

Latex是一种高质量的排版系统,尤其擅长于数学公式的排版。本文我将带大家深入了解Latex在数学公式排版中的应用。从基础的数学符号到复杂的公式布局,我们都会一一讲解,通过本文的学习,你将能够轻松编写出清晰、美观的数学公式&…

洛谷 P3214 [HNOI2011] 卡农

题目传送门 前言 再次败在 d p dp dp 手下,但是数据范围这么小应该是可以看出是 d p dp dp 的(毕竟对于其他组合数的问题数据范围都是 1 0 9 10^9 109 起步)。 思路 题意简化 现有 1 , 2 , 3 , . . . , n − 1 , n 1, 2, 3, ... , n -…

【年份数据类型及使用】

在数据分析中,年份的处理需要根据具体场景选择合适的数据类型,以确保后续分析的准确性和效率。以下是常见的年份数据类型及使用场景: 1. 数值类型(整数或浮点数) 适用场景: 仅需存储年份数值(如 2020, 2023),无需进行日期计算。需要将年份作为连续变量参与数学运算(如…

详解七大排序

目录 一.直接插入排序 (1)基本思想 (2)算法步骤 (3)代码实现 (4)算法特性 (5)算法优化 (6)示例演示 二.希尔排序 &#xff08…

YOLOv12 训练从这里开始:LabelImg 标注数据集

视频讲解: YOLOv12 训练从这里开始:LabelImg 标注数据集 labelimg https://github.com/tzutalin/labelImg sudo apt-get install pyqt5-dev-tools pip3 install lxml git clone https://github.com/tzutalin/labelImg.git cd labelImg 开始编译 make…

Day2:前端项目uniapp壁纸实战

先来做一个轮番图。 效果如下&#xff1a; common-style.css view,swiper,swiper-item{box-sizing: border-box; } index.vue <template><view class"homeLayout"><view class"banner"><swiper circular indicator-dots autoplay…

SAP-ABAP:ABAP `LEAVE LIST-PROCESSING` 深度解析

ABAP LEAVE LIST-PROCESSING 深度解析 核心机制 模式切换(Dialog → List) 中断屏幕流 强制终止当前Dialog程序的PBO/PAI处理,脱离屏幕序列控制(如事务码SE38执行的程序)。触发报表事件 激活类报表程序的事件链:INITIALIZATION → AT SELECTION-SCREEN → START-OF-SEL…

在PyTorch中使用GPU加速:从基础操作到模型部署

本文将通过具体代码示例&#xff0c;详细介绍如何在PyTorch中利用GPU进行张量计算和模型训练&#xff0c;包含设备查询、数据迁移以及模型部署等完整流程。 1. 查看GPU硬件信息 使用 nvidia-smi 命令检查GPU状态和进程信息&#xff1a; # 查看GPU信息 !nvidia-smi 输出示例&…

lib-zo,C语言另一个协程库,dns协程化, gethostbyname

lib-zo,C语言另一个协程库,dns协程化, gethostbyname 另一个 C 协程库 https://blog.csdn.net/eli960/article/details/146802313 本协程库 支持 DNS查询 协程化. 禁用所有 UDP 协程化 zvar_coroutine_disable_udp 1;禁用 53 端口的UDP 协程化 zvar_coroutine_disable_ud…

Java第三节:新手如何用idea创建java项目

作者往期文章&#xff1a; Java第一节&#xff1a;debug如何调试程序&#xff08;附带源代码&#xff09;-CSDN博客 Java第二节&#xff1a;debug如何调试栈帧链&#xff08;附带源代码&#xff09;-CSDN博客 步骤一 ​ 步骤二 ​ 步骤三 创建src文件夹包含main文件&#…

(一)从零开始:用 LangChain 和 ZhipuAI 搭建简单对话

最近一直在研究如何用 LangChain 和 ZhipuAI 搭建一个智能对话系统&#xff0c;发现这个组合真的非常强大&#xff0c;而且实现起来并不复杂。今天就来分享一下我的学习过程和一些心得体会&#xff0c;希望能帮到同样在探索这个领域的小伙伴们。 一、 环境搭建&#xff1a;从零…

uniapp地图导航及后台百度地图回显(v2/v3版本)

百度地图申请地址&#xff1a;控制台 | 百度地图开放平台 效果&#xff1a; 1.后台 1.1申请百度地图APi 1.2 引入百度地图 <script type"text/javascript" src"//api.map.baidu.com/api?v3.0&ak自己百度生气apikey"></script> 1.3 v2组…

Java模板方法模式详解

模板方法模式详解 一、模式定义 模板方法模式(Template Method Pattern)定义一个操作中的算法骨架&#xff0c;将某些步骤延迟到子类实现。 二、核心结构 1. 抽象模板类 public abstract class AbstractTemplate {// 模板方法&#xff08;final防止子类覆盖&#xff09;pu…

(5)模拟后——Leonardo的可视化操作

1 引言 经过n天的模拟&#xff0c;模拟结果相信已经到手&#xff0c;但如何进行分析呢。 首先是可视化&#xff0c;可视化方法基本分为两类 基于ENVI-met自带软件Leonardo的可视化操作基于NetCDF的可视化操作 2 模拟结果变量说明 首先&#xff0c;模拟结果会有以下几个文件…