双向链表介绍

目录

1. 双向链表的结构

2. 双向链表的实现

初始化哨兵位:

打印链表:

尾插:

头插:

尾删:

头删:

查找:

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

删除目标位置的数据:

销毁链表:


1. 双向链表的结构

双向链表也叫双链表,是链表的一种,它的每个数据节点中都有两个指针,分别指向直接后继和直接前驱。所以,从双向链表中的任意一个结点开始,都可以很方便地访问它的前驱结点和后继结点。一般我们都构造双向循环链表。

带头双向循环链表: 

注意:这里的“带头”指的是“哨兵位”,哨兵位不存储任何的有效元素。

哨兵位的意义在于,遍历循环链表避免出现死循环。

当链表中只有哨兵位时,称该链表为空链表。

2. 双向链表的实现

根据上图,可以写出双向链表的结构:

typedef int LTDataType;
typedef struct ListNode
{struct ListNode* next;//后继struct ListNode* prev;//前驱LTDataType data;
}LTNode;

初始化哨兵位:

老样子使用 malloc() 开辟空间,哨兵位的初始值可以是随便的数据。

双向链表在初始的时候只有哨兵位 phead 一个节点,哨兵位也有它的前驱指针和后继指针。因为双向链表是循环的,所以有:

使用一级指针,形参的改变不能影响实参。使用二级指针的目的就是对实参产生影响。

为了将哨兵位的初始值定义为-1,所以使用二级指针。 

具体代码为:

//List.h//定义双向链表中节点的结构
typedef int LTDataType;
typedef struct ListNode
{struct ListNode* next;struct ListNode* prev;LTDataType data;
}LTNode;//注意,双向链表是带有哨兵位的,使用之前必须初始化一个哨兵位
void LTInit(LTNode** pphead);
//List.cvoid LTInit(LTNode** pphead)
{*pphead = (LTNode*)malloc(sizeof(LTNode));if (*pphead == NULL){perror("malloc fail");exit(1);}(*pphead)->data = -1;//初始化一个随便的值(*pphead)->next = (*pphead)->prev = *pphead;
}

测试初始化:

 这是传参数的写法,如果不传参数呢?

环形链表的约瑟夫问题 中,通过返回值的形式将链表的头/尾返回。

同样的,在进行双向链表的初始化时,就可以不传参数,通过返回值的形式把创建好的哨兵位返回。

//List.hLTNode* LTInit();
//List.c//创建新节点
LTNode* LTBuyNode(LTDataType x) 
{LTNode* newnode = (LTNode*)malloc(sizeof(LTNode));if (newnode == NULL) {perror("malloc fail!");exit(1);}newnode->data = x;newnode->next = newnode->prev = newnode;return newnode;
}//创建哨兵位
LTNode* LTInit() 
{LTNode* phead = LTBuyNode(-1);return phead;
}

打印链表:

//List.cvoid LTPrint(LTNode* phead) 
{assert(phead);//遍历链表LTNode* pcur = phead->next;while (pcur != phead){printf("%d->", pcur->data);pcur = pcur->next;}printf("\n");
}

 注意:这里pcur 不指向哨兵位是为了避免死循环,所以选择指向哨兵位的下一位。

尾插:

首先要注意,不管插入还是删除都不能影响到哨兵位。

所以,这里将使用一级指针。

//List.h//不需要改变哨兵位,则不需要传二级指针
//如果需要修改哨兵位的话,则传二级指针
void LTPushBack(LTNode* phead, LTDataType x);

 尾插示意图:

很显然,尾插影响到的节点有:newnode、ptail、哨兵位。

从图中可知,双向链表的尾节点 ptail 就是 head->prev 。

所以,和单链表不同的是,双向链表找尾节点不需要遍历链表!

示例代码:

//List.c//尾插
void LTPushBack(LTNode* phead, LTDataType x) 
{assert(phead);LTNode* newnode = LTBuyNode(x);//顺序为 phead   phead->prev(ptail)  newnodenewnode->next = phead;newnode->prev = phead->prev;phead->prev->next = newnode;phead->prev = newnode;
}

 测试尾插:

头插:

注意:头插指的是在第一个有效节点之前插入新节点!

如果在哨兵位之前插入,就相当于尾插!

头插示意图:

很显然,头插影响到的节点有:哨兵位、newnode、head->next。

先将 newnode->next 指向 head->next,newnode->prev 指向 head;

然后再改变 head->next 指向 newnode,head->next->prev 指向 newnode。

让 newnode 成为新的 head->next。

//List.h//头插
void LTPushFront(LTNode* phead, LTDataType x);
//List.c//头插
void LTPushFront(LTNode* phead, LTDataType x) 
{assert(phead);LTNode* newnode = LTBuyNode(x);//phead newnode phead->nextnewnode->next = phead->next;newnode->prev = phead;phead->next->prev = newnode;phead->next = newnode;
}

测试头插:

尾删:

尾删示意图:

很显然,尾删影响到的节点有:ptail(head->prev)、ptail->prev、哨兵位。

先将 head->prev->prev(图中就是d2)->next 指向 head;

然后将 head->prev 指向 head->prev->prev;

最后将原来的尾节点(d3)释放。

//List.h//尾删
void LTPopBack(LTNode* phead);
//List.c//尾删
void LTPopBack(LTNode* phead) 
{assert(phead);//链表为空:只有一个哨兵位节点assert(phead->next != phead);LTNode* del = phead->prev;LTNode* prev = del->prev;prev->next = phead;phead->prev = prev;free(del);del = NULL;
}

 测试尾删:

头删:

一样的,头删删除的是第一个有效的节点!

头删示意图:

很显然,头删影响到的节点有:head->next->next、head->next、哨兵位。

先将 head->next->next(就是d2)->prev 指向 head;

然后将 head->next 指向 head->next->next;

最后将原来的第一个节点(d1)释放。

//List.h//头删
void LTPopFront(LTNode* phead);
//List.c//头删
void LTPopFront(LTNode* phead) 
{assert(phead);assert(phead->next != phead);LTNode* del = phead->next;LTNode* next = del->next;next->prev = phead;phead->next = next;free(del);del = NULL;
}

 测试头删:

查找:

很简单,遍历链表,有则返回,无则NULL。

//List.h//查找
LTNode* LTFind(LTNode* phead, LTDataType x);
//List.c//查找
LTNode* LTFind(LTNode* phead, LTDataType x) 
{assert(phead);LTNode* pcur = phead->next;while (pcur != phead){if (pcur->data == x) {return pcur;}pcur = pcur->next;}return NULL;
}

 测试查找:

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

插入示意图:

很显然,插入影响到的节点有:pos、pos->next。

但是要注意,如果先将 pos->next 指向 newnode,然后 pos->next->prev 指向 newnode,这个顺序是错误的,会导致找不到原来的 pos->next。

示例代码:

//List.h//在pos位置之后插入数据
void LTInsert(LTNode* pos, LTDataType x);
//List.c//在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;
}

测试插入:

删除目标位置的数据:

删除示意图:

很显然,删除影响到的节点有:pos、pos->next、pos->prev。

示例代码:

//List.h//删除pos位置的数据
void LTErase(LTNode* pos);
//List.c//删除pos位置的数据
void LTErase(LTNode* pos) 
{assert(pos);pos->next->prev = pos->prev;pos->prev->next = pos->next;free(pos);pos = NULL;
}

测试删除: 

销毁链表:

链表的销毁其实就是把链表里的数据一个个销毁。

多了要将哨兵位销毁的操作。

示例代码:

//List.h//销毁
void LTDesTroy(LTNode* phead);
//List.cvoid LTDesTroy(LTNode* phead) 
{//哨兵位不能为空assert(phead);LTNode* pcur = phead->next;while (pcur != phead){LTNode* next = pcur->next;free(pcur);pcur = next;}//链表中只有一个哨兵位free(phead);phead = NULL;
}

自行调试。

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

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

相关文章

CAXA电子图版2020版 下载地址及安装教程

CAXA电子图板是一款由国内软件公司CAXA开发的专业CAD(计算机辅助设计)软件。它主要用于绘制和编辑各种类型的二维图纸和工程图纸,广泛应用于建筑、机械、电气和电子等行业。 CAXA电子图板具有以下主要功能和特点: 二维绘图&…

什么是自动化测试?深入了解其作用!

本文介绍了自动化测试的概念,以及为什么重要。自动化测试可以帮助你节省时间和金钱,并提高你的软件质量 自动化测试是指利用工具模拟人为操作,自动执行测试用例,并生成测试结果的过程。它是一种高效、准确、一致的测试方法&#…

C++手撕红黑树

文章目录 红黑树概念性质(条件限制)节点的定义红黑树的结构红黑树的插入cur为红,p为红,g为黑,u存在且为红cur为红,p为红,g为黑,u不存在或u为黑,插入到p对应的一边cur为红…

02 _ 分布式系统的指标:啥是分布式的三围

你好,我是聂鹏程。 在上一篇文章中,通过对分布式发展历程的学习,我们对分布式技术有了一个整体印象。接下来,我们就再来看看可以用哪些指标去具体地衡量一个分布式系统。如果你已经对分布式系统的指标了解得很清楚了,…

群晖NAS使用Docker部署Potopea在线图片编辑工具并实现公网访问

文章目录 1. 部署Photopea2. 运行Photopea3. 群晖安装Cpolar4. 配置公网地址5. 公网访问测试6. 固定公网地址 本文主要介绍如何在群晖NAS使用Docker部署Potopea在线图片编辑工具,并结合cpolar内网穿透实现公网环境可以远程访问本地部署的Potopea. Photopea是一款强大…

第十五届蓝桥杯测试组模拟赛两期

文章目录 功能测试一期-场景法-登录功能一期-等价类-边界值-添加用户账号输入框一期-登录-缺陷报告一期- UI自动化测试一期-单元测试-路径覆盖二期-正交法-搜索条件组合二期-测试用例二期-缺陷报告二期-自动化测试二期-单元测试-基本路径覆盖 功能测试 一期-场景法-登录功能 …

【云呐】工单管理流程,工单管理怎么处理

工单创建  客户或内部员工在系统中创建工单。工单应包括以下信息:  问题的描述  工单的优先级和紧急程度  相关的客户或内部员工信息  工单的类型或类别  相关的附件或文件 工单分配  工单需要分配给适当的人员或团队来解决。分配过程可能涉及到以下步…

MySQL 优化总结

目标知识 MySQL执行流程图 MySQL 优化成本路线图 优化成本&#xff1a;硬件>系统配置>数据库表结构>SQL及索引。优化效果&#xff1a;硬件<系统配置<数据库表结构<SQL及索引。 MySQL 五大优化原则 减少数据返回&#xff1a;设置合理字段数据类型、启用压缩…

Vue-B站学习笔记

1. 路由配置 B站视频之Vue route文件下的index.js app.vue

js获取上周本周下周的日期(附Demo)

目录 前言1. 基本知识2. Demo3. 彩蛋 前言 现在的时间点是&#xff1a;2024-04-08&#xff0c;对应的日期如下&#xff08;上周、这周、下周&#xff09; 1. 基本知识 讲述Demo之前&#xff0c;先补充一些基础知识 JavaScript 中的 Date 对象是用于处理日期和时间的对象。它…

提问:一台交换机能带动多少个网络监控摄像头?

你们好&#xff0c;我的网工朋友。 标题的这个问题呢&#xff0c;其实有点问题的。因为这一个问题&#xff0c;需要有一堆条件加上&#xff0c;才好判断。 比如&#xff0c;你的交换机是百兆的还是千兆的&#xff1f; 整机的POE功率和端口POE功率是多少&#xff1f; 交换机…

【C语言】青蛙跳台阶问题

题目&#xff1a;一只青蛙一次可以跳上1级台阶&#xff0c;也可以跳上2级台阶。现求该青蛙跳上一个 n 级的台阶总共有多少种跳法。 题目分析&#xff1a; 当 n 等于 1 时&#xff0c;青蛙只能跳一级台阶到达&#xff0c;因此只有一种跳法&#xff0c;直接返回 1。当 n 等于 2 时…

【Android】App通信基础架构相关类源码解析

应用通信基础架构相关类源码解析 这里主要对Android App开发时&#xff0c;常用到的一些通信基础类进行一下源码的简单分析&#xff0c;包括&#xff1a; Handler&#xff1a;处理器&#xff0c;与某个Looper&#xff08;一个线程对应一个Looper&#xff09;进行关联。用于接…

最少按键次数

题目描述 给你一个字符串 s&#xff0c;由小写英文字母组成。 电话键盘上的按键与 不同 小写英文字母集合相映射&#xff0c;可以通过按压按键来组成单词。例如&#xff0c;按键 2 对应 ["a","b","c"]&#xff0c;我们需要按一次键来输入 &quo…

【javaWeb 原理篇】底层实现原理(快速学习配置原理,Bean管理)

Spring底层 配置优先级Bean管理获取beanBean的作用域第三方Bean SpringBoot原理起步依赖自动配置自动配置的原理自定义starter 配置优先级 Spring中的配置文件如果配置了相同的内容则根据配置优先级进行配置: application.properties>application.yml>application.yaml …

用Python+OpenCV截取视频中所有含有字幕的画面

1、需求背景 有的视频文件的字幕已经压制到了视频的图像中&#xff0c;不能单独提取出字幕文件。网上的 “提取视频字幕” 网站多为提取视频中的字幕文件&#xff0c;而非识别视频图像中的字幕。少数通过OCR技术识别画面中字幕的工具需要在线运行、运行速度较慢&#xff0c;或…

蓝桥杯练习笔记(十八)

蓝桥杯练习笔记&#xff08;十八&#xff09; 一、用辅助栈来优化递归深度过大的问题 输入示例 0000100010000001101010101001001100000011 0101111001111101110111100000101010011111 1000010000011101010110000000001011010100 0110101010110000000101100100000101001001 0…

QT打包生成.exe可执行文件

QT打包生成.exe可执行文件 程序运行图标如何设置快捷方式显示图标QT打包成可执行文件将可执行文件打包成安装包程序运行图标 如何生成如下图标? 首先将你的图标(ico文件)放入当前工程目录,即含有.pro文件的同级目录 右击项目,选择ADD New,选择Qt Resource File, 这是一个…

吴恩达2022机器学习专项课程(一) 5.7 检测梯度下降是否收敛

问题预览/关键词 什么是梯度下降收敛&#xff1f;哪些方法可以检测梯度下降是否收敛&#xff1f;什么是学习曲线&#xff1f;曲线上升代表什么&#xff1f;什么原因造成的&#xff1f;如何检测梯度下降是否收敛&#xff1f;多少次迭代&#xff0c;梯度下降会收敛&#xff1f;什…

C++:初步接触C++(2)

hello&#xff0c;各位小伙伴&#xff0c;本篇文章跟大家一起学习C&#xff0c;感谢大家对我上一篇的支持&#xff0c;如有什么问题&#xff0c;还请多多指教 &#xff01; 文章目录 内联函数1.概念2.特性 auto关键字1.auto简介2.auto的使用细则3.auto不能推导的场景 基于范围…