【手撕数据结构】拿捏双向链表

目录

  • 链表介绍
  • 初始化链表
  • 销毁链表
  • 查找节点
  • 打印链表
  • 增加节点
    • 尾插
    • 头插
    • 在指定位置之后插入节点
  • 删除节点
    • 尾删
    • 头删
    • 删除指定位置节点
  • 链表判空

链表介绍

前面说到,链表的结构一共有八种:带头单向循环链表、带头单向非循环链表、带头双向循环链表、带头双向非循环链表、无头单向循环链表、无头单向非循环链表、无头双向循环链表、无头双向非循环链表。
 在这八种结构中,我们只挑两种来进行刨析,即无头单向非循环链表和带头双向循环链表。
在这里插入图片描述
 而今天我们要介绍的双向链表就是带头的,头结点head也被称为哨兵位,如果链表为空,则链表只有一个哨兵位。双向链表的结构为

typedef int LTDataType;	//方便以后修改双向链表存储的数据类型
typedef struct ListNode
{LTDataType val;struct ListNode* prev;struct ListNode* next;
}LTNode;

分别用一个变量存储数据,前驱指针prev指向前节点,next指针指向后节点,这样链表就可以进行随机访问。

初始化链表

LTNode* LTBuyNode(LTDataType x)		//尾插,头插等都需要创建新的节点,不妨封装一个函数,每个节点的next,prev指针都先指向自己,
{		LTNode* newnode = (LTNode*)malloc(sizeof(LTDataType));if (newnode == NULL){perror("malloc failed");exit(1);}newnode->val = x;newnode->prev = newnode->next = newnode;return newnode;
}

因为插入都需要创建节点,所以封装一个函数
注意:这些节点的next指针和prev指针都指向自己,这是为了兼容空链表只有一个哨兵位时,和后面插入节点时利用这两个指向自己的指针调成被影响的节点的指针

//void LTInit(LTNode** pphead)
//{
//	assert(pphead);
//	*pphead = LTBuyNode(-1);
//}
//优化接口一致性版本
LTNode* LTInit(LTNode* phead)
{LTNode* newnode = LTBuyNode(-1);return newnode;
}

首先,我们要对链表进行初始化,上面说了空双向链表是只有一个哨兵位,所以我们创建一个节点作为哨兵位.

销毁链表

//void LTDestroy(LTNode** pphead)
//{
//	assert(pphead && *pphead);	//*pphead防止释放空链表
//	LTNode* pcur = (*pphead)->next;
//	while (pcur != *pphead)
//	{
//		LTNode* Next = pcur->next;
//		free(pcur);
//		pcur = NULL;
//		pcur = Next;
//	}
//	free(*pphead);
//	*pphead = NULL;
//}
//优化接口一致性版本
void LTDestroy(LTNode* pphead)
{assert(pphead);LTNode* pcur = pphead->next;while (pcur != pphead){LTNode* Next = pcur->next;free(pcur);pcur = NULL;pcur = Next;}free(pphead);pphead = NULL;	//这里设置NULL了不影响实参,需要手动设置NULL,但是释放了空间是同一块空间,
}

销毁链表,从头结点的后一个结点处开始向后遍历并释放结点,直到遍历到头结点时,停止遍历并将头结点也释放掉。

查找节点

LTNode* LTFind(LTNode* phead, LTDataType x)
{assert(phead);if (!LTEmpty(phead)){return NULL;}else{LTNode* pcur = phead->next;while (pcur != phead){if (pcur->val == x){return pcur;}pcur = pcur->next;}}return NULL;
}

给定一个值,在链表中寻找与该值相同的结点,若找到了,则返回结点地址;若没有找到,则返回空指针(NULL)。如果为空链表,就直接返回NULL.这里因为双向楼面是循环的,用pcur从头结点(哨兵位下一个节点)开始遍历,如果回到哨兵位表示已经查找完一圈,没找到。

打印链表

void LTPrint(LTNode* phead)
{assert(phead);LTNode* pcur = phead->next;while (pcur != phead){printf("%d->", pcur->val);pcur = pcur->next;}
}

打印双向链表时也是从头结点的后一个结点处开始向后遍历并打印,直到遍历哨兵位结点处时便停止遍历和打印(哨兵位结点数据不打印)。

增加节点

尾插

void LTPushBack(LTNode* phead, LTDataType x)
{assert(phead);LTNode* newnode = LTBuyNode(x);newnode->next = phead;newnode->prev = phead->prev;phead->prev->next = newnode;phead->prev = newnode;
}

尾插,申请一个新结点,将节点插入到旧尾节点之后,改变旧尾节点的next指针和新尾节点的next指针和prev指针,改变哨兵为的prev指针

在这里插入图片描述

头插

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 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 LTPopBack(LTNode* phead)
{assert(phead);assert(LTEmpty(phead));LTNode* del = phead->prev;del->prev->next = phead;phead->prev = del->prev;free(del);del = NULL;
}

头删

头删,即释放哨兵位的后一个结点,并建立哨兵位节点与被删除结点的后一个结点之间的双向关系即可。
在这里插入图片描述

void LTPopFront(LTNode* phead)
{assert(phead);assert(LTEmpty(phead));LTNode* del = phead->next;phead->next = del->next;del->next->prev = phead;free(del);del = NULL;
}

删除指定位置节点

void LTErase(LTNode* pos)
{assert(pos);pos->prev->next = pos->next;pos->next->prev = pos->prev;free(pos);pos = NULL;
}

删除指定位置结点,释放掉目标结点后,建立该结点前一个结点和后一个结点之间的双向关系即可。

链表判空

bool LTEmpty(LTNode* phead)
{assert(phead);return phead->next != phead;
}

链表判空,即判断头结点的前驱或是后驱指向的是否是自己即可(因为是循环)

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

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

相关文章

【初阶数据结构】5.栈和队列

文章目录 1.栈1.1 概念与结构1.2 栈的实现2.队列2.1 概念与结构2.2 队列的实现3.栈和队列算法题3.1 有效的括号3.2 用队列实现栈3.3 用栈实现队列3.4 设计循环队列 1.栈 1.1 概念与结构 栈:一种特殊的线性表,其只允许在固定的一端进行插入和删除元素操…

C语言宏定义格式化控制台打印

写了个简单的控制台打印代码&#xff0c;有三种打印级别 DEBUG INFO ERROR&#xff0c;支持颜色打印&#xff0c;支持时间打印 在MSVC环境中使用 #include <time.h> #include <string.h> #include <stdio.h>/* log level */ #define LOG_LEVEL_DEBUG (1) #d…

【STM32 HAL库】全双工I2S+双缓冲DMA的使用

1、配置I2S 我们的有效数据是32位的&#xff0c;使用飞利浦格式。 2、配置DMA **这里需要注意&#xff1a;**i2s的DR寄存器是16位的&#xff0c;如果需要发送32位的数据&#xff0c;是需要写两次DR寄存器的&#xff0c;所以DMA的外设数据宽度设置16位&#xff0c;而不是32位。…

Log4j2原理及应用详解(十三)

本系列文章简介&#xff1a; 在软件开发过程中&#xff0c;日志记录是一个不可或缺的重要环节。它不仅帮助开发者在开发阶段追踪和调试代码&#xff0c;还在软件运行阶段提供了宝贵的运行信息和错误追踪能力。随着软件系统的日益复杂&#xff0c;对日志记录的需求也变得越来越高…

8.3 End-to-end Data Protection (Optional)

8.3 End-to-end Data Protection (Optional) 为了提供从应用程序到NVM介质并返回到应用程序本身的稳健数据保护,可以使用端到端数据保护。如果启用了此可选机制,则将额外的保护信息(例如CRC)添加到逻辑块中,控制器和/或主机软件可以对其进行评估,以确定逻辑块的完整性。…

大模型能干什么

大模型是指具有庞大参数量和复杂结构的机器学习模型。相比于传统的小模型&#xff0c;大模型通常具有更强的表达能力和更高的预测准确性&#xff0c;其在机器学习和人工智能领域中扮演着至关重要的角色&#xff0c;它们能够处理大规模数据和复杂模型&#xff0c;具有广泛的应用…

一文带你读懂MLIR论文,理解MLIR设计准则.

论文MLIR: Scaling Compiler Infrastructure for Domain Specific Computation MLIR&#xff1a;针对特定领域计算扩展编译器基础设施 文章目录 论文MLIR: Scaling Compiler Infrastructure for Domain Specific Computation1. 论文下载2. TVM关于MLIR的讨论3. 论文正文0. 摘要…

02互联网行业的产品方向(2)

数字与策略产品 大数据时代&#xff0c;数据的价值越来越重要。大多数公司开始对内外全部数据进行管理与挖掘&#xff0c;将业务数据化&#xff0c;数据资产化&#xff0c;资产业务化&#xff0c;将数据产品赋能业务&#xff0c;通过数据驱动公司业务发展&#xff0c;支撑公司战…

关于Hadoop生态系统一部分的Catalog

Catalog在大数据处理和分析的上下文中&#xff0c;它不是一个独立的实体或系统&#xff0c;而是数据处理框架&#xff08;如Apache Flink、Apache Hive、Apache Spark等&#xff09;内部的一个组件。它的主要职责是管理和提供数据集的元数据&#xff0c;使得这些框架可以透明地…

Unity VR开发入门:探索虚拟现实世界的无限可能

目录 引言 Unity VR开发基础 1. 安装Unity与VR SDK 2. 创建VR项目 3. 理解VR场景结构 Unity VR开发实战 1. 场景搭建 2. 交互设计 创建C#脚本 编写VRInteractor脚本 应用脚本到场景 注意 修改VRInteractor脚本 3. 用户体验优化 4. 测试与调试 引言 随着科技的飞速…

docker: No space left on device处理与迁移目录

简介&#xff1a;工作中当遇到Docker容器内部的磁盘空间已满。可能的原因包括日志文件过大、临时文件过多或者是Docker容器的存储卷已满&#xff0c;需要我们及时清理相关文件&#xff0c;并对docker的路径进行迁移。 历史攻略&#xff1a; centos&#xff1a;清理磁盘空间 …

知识库问答研究进展与展望

基于知识库的问答(QuestionAnsweringoverKnowledgeBase,KBQA)是问答系统的重要组成部分,要求计算机正确理解自然语言问题的语义,并从知识库中提取问题的答案.早期研究主要关注仅涉及到单个关系三元组的简单问答,近年来,随着以深度学习为代表的表示学习技术在简单问答任务的成功…

Https post 请求时绕过证书验证方案

解决异常&#xff1a;Caused by: java.security.cert.CertificateException: No subject alternative names matching IP address xxx.xx.xx.xx found // Https POST 请求private cn.hutool.json.JSON PostGsData(String url, String appKey, String token, Map<String, Ob…

VMware 虚拟机 ping 不通原因排查

目录 一、检查网络 二、重启虚拟机网络 因为最近遇到了一个比较奇怪的 ping 不通虚拟机的事&#xff0c;在此过程中&#xff0c;检查了很多的设置&#xff0c;故而写一篇文章记录下&#xff0c;如有 VMware 虚拟机 ping 不通可以尝试本文的排查方式。 下面以 VMware 虚拟机为…

Day03-Pod环境变量,容器重启策略,emptyDir,hostPath,nfs存储卷,资源限制及configMap,secret实战案例

Day03-Pod环境变量&#xff0c;容器重启策略&#xff0c;emptyDir&#xff0c;hostPath&#xff0c;nfs存储卷&#xff0c;资源限制及configMap&#xff0c;secret实战案例 0、昨日内容回顾:1、面试题预告1.1 Q1&#xff1a;Pod的容器的三种重启策略:&#xff08;注意&#xff…

数学建模--灰色关联分析法

目录 简介 基本原理 应用场景 优缺点 优点&#xff1a; 缺点&#xff1a; 延伸 灰色关联分析法在水质评价中的具体应用案例是什么&#xff1f; 如何克服灰色关联分析法在主观性强时的数据处理和改进方法&#xff1f; 灰色关联分析法与其他系统分析方法&#xff08;如A…

记录些MySQL题集(17)

一、MySQL索引为何使用B树结构&#xff1f; MySQL的索引机制中&#xff0c;默认使用BTree作为底层的数据结构&#xff0c;但为什么要选择B树呢&#xff1f;有人会说树结构是以二分法查找数据&#xff0c;所以会在很大程度上提升检索性能&#xff0c;这点确实没错&#xff0c;但…

C++初学者指南-5.标准库(第一部分)--标准库查询存在算法

C初学者指南-5.标准库(第一部分)–标准库查询存在算法 文章目录 C初学者指南-5.标准库(第一部分)--标准库查询存在算法any_of / all_of / none_ofcountcount_if相关内容 不熟悉 C 的标准库算法&#xff1f; ⇒ 简介 any_of / all_of / none_of 如果在输入范围(所有元素…

解决django与sqlite3不兼容报SQLite 3.9.0 or later is required错的问题

今天在尝试用pytest进行django的单元测试&#xff0c;pytest用的数据库是sqlite3&#xff0c;在window环境下测试得好好的&#xff0c;但是放到linux环境下就报错&#xff0c;具体是报django.core.exceptions.ImproperlyConfigured: SQLite 3.9.0 or later is required (found …

GPT-LLM

本心、输入输出、结果 文章目录 GPT-LLM前言国际公司AI发展概览国内公司AI发展概览GPT-LLM 编辑 | 简简单单 Online zuozuo 地址 | https://blog.csdn.net/qq_15071263 如果觉得本文对你有帮助,欢迎点赞、收藏、评论 前言 国际公司AI发展概览 公司主要AI贡献与产品特点OpenAI…