C语言:双链表
一、什么是双链表?

双链表,顾名思义,是一种每个节点都包含两个链接的链表:一个指向下一个节点,另一个指向前一个节点。这种结构使得双链表在遍历、插入和删除操作上都表现出色。与单链表相比,双链表不仅可以从头节点开始遍历,还可以从尾节点开始遍历,甚至从中间某个节点开始双向遍历。
二、双链表的特点
双向性:每个节点都包含两个指针,一个指向前一个节点,一个指向后一个节点。这使得双链表在遍历上更加灵活。
动态性:链表的大小可以根据需要动态地增加或减少,无需预先分配固定大小的内存空间。
三、实现双链表
typedef int LTDataType;
typedef struct ListNode
{LTDataType data;struct ListNode* next;struct ListNode* prev;
}LTNode;
三、实现的功能
LTNode* LTInit();// 初始化双链表
void LTDestroy(LTNode* phead);//销毁
void LTPrint(LTNode* phead);//打印
bool LTEmpty(LTNode* phead);//判断链表是否为空·void LTPushBack(LTNode* phead, LTDataType x);//尾插
void LTPopBack(LTNode* phead);//尾删void LTPushFront(LTNode* phead, LTDataType x);//头插
void LTPopFront(LTNode* phead);//头删
//在pos位置之后插入数据
void LTInsert(LTNode* pos, LTDataType x);
void LTErase(LTNode* pos);//指定删除
LTNode* LTFind(LTNode* phead, LTDataType x);//查找
1.创建节点
// 创建新的双链表节点
LTNode* LTBuyNode(LTDataType x) { LTNode* newNode = (LTNode*)malloc(sizeof(LTNode)); if (newNode == NULL) { perror("malloc fail!"); exit(1); } newNode->data = x; newNode->next = NULL; newNode->prev = NULL; return newNode;
}
使用malloc函数在堆上动态地分配内存空间,以存储LTNode结构体的大小。
检查malloc是否成功分配了内存。如果返回NULL,表示内存分配失败,此时调用perror函数打印错误消息,并使用exit(1)退出程序。
如果内存分配成功,将新节点的数据成员data设置为参数x的值。
初始化新节点的next和prev指针为NULL,表示这个新节点在创建时并不指向任何其他的节点。
返回指向新创建节点的指针。
2.初始化
LTNode* LTInit() { LTNode* pheda = LTBuyNode(-1); // 使用-1作为哨兵位头节点的数据 pheda->next = pheda; // 指向自己,表示链表为空 pheda->prev = pheda; return pheda;
}
3.双链表的尾插
void LTPushBack(LTNode* phead, LTDataType x)
{assert(phead);LTNode* newnode = LTBuyNode(x);// 创建一个新节点newnode->prev = phead->prev;newnode->next = phead;phead->prev->next = newnode;phead->prev = newnode;}
newnode->prev = phead->prev; 将新节点的prev指针设置为当前链表的尾节点。
newnode->next = phead; 将新节点的next指针设置为头节点。phead->prev->next = newnode; 更新当前尾节点的next指针,使其指向新节点。
phead->prev = newnode; 更新头节点的prev指针,使其指向新节点
4.双链表的尾删
//尾删
void LTPopBack(LTNode* phead)
{assert(phead && phead->next != phead);LTNode* del = phead->prev;del->prev->next = phead;phead->prev = del->prev;free(del);del = NULL;
}
prev指针指向链表的最后一个节点
del->prev->next = phead; 和 phead->prev = del->prev; 这两行代码更新了链表的链接,将尾节点从链表中移除。
5.双链表的头插
//头插
void LTPushFront(LTNode* phead, LTDataType x)
{assert(phead);LTNode* newnode = LTBuyNode(x);newnode ->next = phead->next;newnode->prev = phead;phead->next->prev = newnode;phead->next = newnode;
}
newnode ->next = phead->next;将新节点的next指针指向当前链表的第一个节点
newnode->prev = phead;将新节点的prev指针指向链表的头节点
phead->next->prev = newnode;更新当前链表第一个节点的prev指针,使其指向新节点。
phead->next = newnode;:更新链表的头节点的next指针,使其指向新节点,这样新节点就成为了链表的第一个节点。
6.双链表的头删
void LTPopFront(LTNode* phead)
{assert(phead && phead->next != phead);LTNode* del = phead->next;phead->next = del->next;phead->next->prev = phead;free(del);del = NULL;}
LTNode* del = phead->next;:将待删除的节点的地址赋给del指针。
phead->next = del->next;:更新链表的头节点的next指针,使其跳过待删除的节点,直接指向下一个节点。
phead->next->prev = phead;:由于我们刚刚更新了phead->next,现在它指向的是原第一个节点的下一个节点。我们将这个新节点的prev指针更新为指向链表的头节点。
7.双链表的打印
void LTPrint(LTNode* phead)
{LTNode* pcur = phead->next;while (pcur != phead){printf("%d ", pcur->data);pcur = pcur->next;}printf("\n");
}
8.在pos位置之后插入数据
void LTInsert(LTNode* pos, LTDataType x)
{assert(pos);LTNode* newnode = LTBuyNode(x); newnode-> next = pos->next;newnode->prev = pos;pos->next->prev = newnode;pos->next = newnode;
}
9.指定删除
void LTErase(LTNode* pos)
{assert(pos);assert(pos != pos->next);pos->prev->next = pos->next;// 更新pos的前一个节点的next指针pos->next->prev = pos->prev;//更新pos的下一个节点的prev指针free(pos);pos = NULL;
}
10.判空
bool LTEmpty(LTNode* phead)
{return phead->next == phead;
}
11.销毁
//销毁
void LTDestroy(LTNode* phead)
{LTNode* cur = phead->next;while (cur != phead){LTNode* tmp = cur;cur = cur->next;free(tmp);}free(phead);
}
四、全部源码
LTNode* LTBuyNode(LTDataType x)
{LTNode* Node = (LTNode*)malloc(sizeof(LTNode));if (Node == NULL){perror("malloc fail!");exit(1);}Node->data = x;Node->next = NULL; Node->prev = NULL; return Node;
}
LTNode* LTInit() {LTNode* pheda = LTBuyNode(-1); // 使用-1作为哨兵头节点的数据 pheda->next = pheda; // 指向自己,表示链表为空 pheda->prev = pheda; return pheda;
}//销毁
void LTDestroy(LTNode* phead)
{LTNode* cur = phead->next;while (cur != phead){LTNode* tmp = cur;cur = cur->next;free(tmp);}free(phead);
}
//打印
void LTPrint(LTNode* phead)
{LTNode* pcur = phead->next;while (pcur != phead){printf("%d ", pcur->data);pcur = pcur->next;}printf("\n");
}
//判断链表是否为空·
bool LTEmpty(LTNode* phead)
{return phead->next == phead;
}
//尾插
void LTPushBack(LTNode* phead, LTDataType x)
{assert(phead);LTNode* newnode = LTBuyNode(x);newnode->prev = phead->prev;newnode->next = phead;phead->prev->next = newnode;phead->prev = newnode;}
//尾删
void LTPopBack(LTNode* phead)
{assert(phead && phead->next != phead);LTNode* del = phead->prev;del->prev->next = phead;phead->prev = del->prev;free(del);del = NULL;
}//头插
void LTPushFront(LTNode* phead, LTDataType x)
{assert(phead);LTNode* newnode = LTBuyNode(x);newnode ->next = phead->next;newnode->prev = phead;phead->next->prev = newnode;phead->next = newnode;
}
//头删
void LTPopFront(LTNode* phead)
{assert(phead && phead->next != phead);LTNode* del = phead->next;phead->next = del->next;phead->next->prev = phead;free(del);del = NULL;}
//查找LTNode* LTFind(LTNode* phead, LTDataType x)
{LTNode* cur = phead->next;while (cur != phead){if (cur->data == x){return cur;}cur=cur->next;}return NULL;
}在pos位置之后插入数据
void LTInsert(LTNode* pos, LTDataType x)
{assert(pos);LTNode* newnode = LTBuyNode(x); newnode-> next = pos->next;newnode->prev = pos;pos->next->prev = newnode;pos->next = newnode;
}//指定删除
void LTErase(LTNode* pos)
{assert(pos);assert(pos != pos->next);pos->prev->next = pos->next;pos->next->prev = pos->prev;free(pos);pos = NULL;
}
五、结语
让我们一起在编程的道路上不断前行,创造更加美好的未来!
相关文章:
C语言:双链表
一、什么是双链表? 双链表,顾名思义,是一种每个节点都包含两个链接的链表:一个指向下一个节点,另一个指向前一个节点。这种结构使得双链表在遍历、插入和删除操作上都表现出色。与单链表相比,双链表不仅可以…...
Java物业管理系统+数据库应用程序开发[JavaSE+JDBC+idea控制台+MySQL]
背景: 使用JavaSEJDBCMySQL技术实现一个物业管理系统,具体要求如下 物业管理系统需求: 需求分析 1.1用户需求分析 在进入系统之前,要进行身份确认,只有用户名和用户密码都相符的用户方可进入本系统,为…...
未在本地计算机上注册“Microsoft.ACE.OLEDB.12.0”提供程序。.net 读取excel的时候报错(实测有效)
1. 下载AccessDatabaseEngine.exe 下载链接 添加链接描述 2. office excel是64为的需要安装【AccessDatabaseEngine.exe】、32位的【AccessDatabaseEngine_X64.exe】 3. 我的是64为,跳过32位安装检测 1. 找到下载的安装包 2.输入安装包文件全称并在后面加上/pas…...
JVM垃圾收集器和性能调优
目标: 1.JVM垃圾收集器有哪几种? 2.CMS垃圾收集器回收步骤。 一、JVM常见的垃圾回收器 为什么垃圾回收的时候需要STW? 标记垃圾的时候,如果不STW,可能用户线程就会不停的产生垃圾。 1.1 单线程收集 Serial和SerialOld使用单…...
汽车EDI——Volvo EDI 项目案例
项目背景 作为Volvo的长期合作伙伴,C公司收到Volvo的EDI对接邀请,需要实现EDI对接。C公司将会面临哪些挑战?又应该相应地选择何种EDI解决方案呢? 汽车行业强调供需双方的高效协同(比如研发设计、生产计划、物流信息等…...
Qt应用程序发布
一、静态编译发布 1.0:以Release模式构建工程 1.1:查看当前构建生成路径,并将所生成的.exe单独拷贝出来 1.2:将可执行文件*.exe拷贝至任一目标文件夹:D:\Temporary\QQIF 2:查看安装Qt时发布工具windeployqt.exe所在的目录 windeployqt.exe在Qt开发套件的bin目录下。Qt的每…...
Python 机器学习 基础 之 【常用机器学习库】 NumPy 数值计算库
Python 机器学习 基础 之 【常用机器学习库】 NumPy 数值计算库 目录 Python 机器学习 基础 之 【常用机器学习库】 NumPy 数值计算库...
Linux Kernel nf_tables 本地权限提升漏洞(CVE-2024-1086)
文章目录 前言声明一、netfilter介绍二、漏洞成因三、漏洞危害四、影响范围五、漏洞复现六、修复方案临时解决方案升级修复方案 前言 2024年1月,各Linux发行版官方发布漏洞公告,修复了一个 netfilter:nf_tables 模块中的释放后重用漏洞(CVE-…...
[word] word如何清除超链接 #媒体#笔记#知识分享
word如何清除超链接 办公中,少不了使用word,这个是大家必备的软件,今天给大家分享下word如何清除超链接的操作办法,一起来学习下吧! 1、清除所有超链接 按下组合键CtrlshiftF9,就可以将网上复制带有超链…...
【Linux】进程(9):进程控制1
大家好,我是苏貝,本篇博客带大家了解Linux进程(9)进程控制1,如果你觉得我写的还不错的话,可以给我一个赞👍吗,感谢❤️ 目录 1 fork函数2 进程终止(A)终止是…...
华为RH2288H V3服务器iBMC的SSL证书续期
本文对华为RH2288H V3服务器iBMC的SSL证书续期,以避名登录告警提示及主机状态异常。 一、检查现网服务器iBMC的SSL证书到期时间 登录iBMC,点击配置--SSL证书,如下: 可以看到本服务器SSL证书将于今年7月22日到期。 二、联系厂家…...
ubuntu开机黑屏
BusyBox v1.30.1 (Ubuntu 1:1.30.1-4ubuntu6.1) built-in shell (ash) Enter help for a list of built-in commands. 解决: help 看看哪个盘出问题了 fsck -y /dev/sda1 (出问题的磁盘/分区) reboot 就可以进入系统了 fsck命令…...
【risc-v】arm和riscv有什么关系或者联系?
ARM和RISC-V都是基于精简指令集计算(RISC)原理的处理器架构,它们在设计理念上有一定的联系,但同时存在一些关键的区别: 设计理念:ARM和RISC-V都采用了RISC的核心设计原则,即通过简化指令集来提高…...
Flutter项目开发模版,开箱即用
前言 当前案例 Flutter SDK版本:3.22.2 每当我们开始一个新项目,都会 引入常用库、封装工具类,配置环境等等,我参考了一些文档,将这些内容整合、简单修改、二次封装,得到了一个开箱即用的Flutter开发模版…...
私有仓库搭建
目前市面上比较常见的私有仓库搭建方法为: 通过 Sinopia 或 verdaccio 搭建(Sinopia 已经停止维护,verdaccio 是 Fork 自 Sinopia,基本上大同小异),其优点是搭建简单,不需要其他服务。通过 cnp…...
axios设置 responseType为 “stream“流式获取后端数据
使用前景: 工作过程中遇到了后端接口响应过慢,前端界面一致loading的情况,这个时候可以尝试采用将Axios的responseType参数被设置为stream类型实现。 stream介绍: stream类型意味着你希望服务器响应的数据以Node.js流ÿ…...
Apache POI(使用Java读写Excel表格数据)
1.Apache POI简介 Apache POI是一个开源的Java库,用于操作Microsoft Office格式的文件。它支持各种Office文档的读写功能,包括Word文档、Excel电子表格、PowerPoint演示文稿、Outlook电子邮件等。Apache POI提供了一组API,使得Java开发者能够…...
golang中只用定义不用初始化的类型规律总结
在go语言的开发中,有很多的内置类型是我们只需要定义而不需要初始化的, 如上文中提到的bytes.Buffer, strings.Builder。 其实在go语言中官方给我们定义的很多的类型都只需要定义,不需要初始化。 他们都有2个共同的规律ÿ…...
数据库之PostgreSQL详解
一、PostgreSQL介绍 PostgreSQL是一个功能强大的 开源 的关系型数据库。底层基于C实现。 PostgreSQL的开源协议和Linux内核版本的开源协议是一样的。。BDS协议,这个协议基本和MIT开源协议一样,说人话,就是你可以对PostgreSQL进行一些封装&a…...
找出链表倒数第k个元素-链表题
LCR 140. 训练计划 II - 力扣(LeetCode) 快慢指针。快指针臂慢指针快cnt个元素到最后; class Solution { public:ListNode* trainingPlan(ListNode* head, int cnt) {struct ListNode* quick head;struct ListNode* slow head;for(int i …...
XML Group端口详解
在XML数据映射过程中,经常需要对数据进行分组聚合操作。例如,当处理包含多个物料明细的XML文件时,可能需要将相同物料号的明细归为一组,或对相同物料号的数量进行求和计算。传统实现方式通常需要编写脚本代码,增加了开…...
Linux应用开发之网络套接字编程(实例篇)
服务端与客户端单连接 服务端代码 #include <sys/socket.h> #include <sys/types.h> #include <netinet/in.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <arpa/inet.h> #include <pthread.h> …...
C# SqlSugar:依赖注入与仓储模式实践
C# SqlSugar:依赖注入与仓储模式实践 在 C# 的应用开发中,数据库操作是必不可少的环节。为了让数据访问层更加简洁、高效且易于维护,许多开发者会选择成熟的 ORM(对象关系映射)框架,SqlSugar 就是其中备受…...
全志A40i android7.1 调试信息打印串口由uart0改为uart3
一,概述 1. 目的 将调试信息打印串口由uart0改为uart3。 2. 版本信息 Uboot版本:2014.07; Kernel版本:Linux-3.10; 二,Uboot 1. sys_config.fex改动 使能uart3(TX:PH00 RX:PH01),并让boo…...
初学 pytest 记录
安装 pip install pytest用例可以是函数也可以是类中的方法 def test_func():print()class TestAdd: # def __init__(self): 在 pytest 中不可以使用__init__方法 # self.cc 12345 pytest.mark.api def test_str(self):res add(1, 2)assert res 12def test_int(self):r…...
Java 二维码
Java 二维码 **技术:**谷歌 ZXing 实现 首先添加依赖 <!-- 二维码依赖 --><dependency><groupId>com.google.zxing</groupId><artifactId>core</artifactId><version>3.5.1</version></dependency><de…...
人机融合智能 | “人智交互”跨学科新领域
本文系统地提出基于“以人为中心AI(HCAI)”理念的人-人工智能交互(人智交互)这一跨学科新领域及框架,定义人智交互领域的理念、基本理论和关键问题、方法、开发流程和参与团队等,阐述提出人智交互新领域的意义。然后,提出人智交互研究的三种新范式取向以及它们的意义。最后,总结…...
Chrome 浏览器前端与客户端双向通信实战
Chrome 前端(即页面 JS / Web UI)与客户端(C 后端)的交互机制,是 Chromium 架构中非常核心的一环。下面我将按常见场景,从通道、流程、技术栈几个角度做一套完整的分析,特别适合你这种在分析和改…...
中科院1区顶刊|IF14+:多组学MR联合单细胞时空分析,锁定心血管代谢疾病的免疫治疗新靶点
中科院1区顶刊|IF14:多组学MR联合单细胞时空分析,锁定心血管代谢疾病的免疫治疗新靶点 当下,免疫与代谢性疾病的关联研究已成为生命科学领域的前沿热点。随着研究的深入,我们愈发清晰地认识到免疫系统与代谢系统之间存在着极为复…...
MySQL基本操作(续)
第3章:MySQL基本操作(续) 3.3 表操作 表是关系型数据库中存储数据的基本结构,由行和列组成。在MySQL中,表操作包括创建表、查看表结构、修改表和删除表等。本节将详细介绍这些操作。 3.3.1 创建表 在MySQL中&#…...
