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

目录

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

链表介绍

前面说到,链表的结构一共有八种:带头单向循环链表、带头单向非循环链表、带头双向循环链表、带头双向非循环链表、无头单向循环链表、无头单向非循环链表、无头双向循环链表、无头双向非循环链表。
 在这八种结构中,我们只挑两种来进行刨析,即无头单向非循环链表和带头双向循环链表。
在这里插入图片描述
 而今天我们要介绍的双向链表就是带头的,头结点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位。…

一文带你读懂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;支撑公司战…

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;清理磁盘空间 …

记录些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…

【LeetCode】day17:654 - 最大二叉树, 617 - 合并二叉树, 700 - 二叉树搜索树中的搜索, 98 - 验证二叉搜索树

LeetCode 代码随想录跟练 Day17 654.最大二叉树617.合并二叉树700.二叉搜索树中的搜索98.验证二叉搜索树 654.最大二叉树 题目描述&#xff1a; 给定一个不重复的整数数组 nums 。 最大二叉树 可以用下面的算法从 nums 递归地构建: 创建一个根节点&#xff0c;其值为 nums 中的…

SpringBoot整合SSE,实现后端主动推送DEMO

前言 说起服务端主动推送&#xff0c;大家第一个想到的一定是WEBSOCKET 。 作为软件工程师&#xff0c;不能无脑使用一种技术&#xff0c;要结合实际情况&#xff0c;择优选取。 SSE&#xff08;Server-Sent Events&#xff09;相比于WEBSOCKET 1、轻量化、兼容性 基于传统…

pytorch学习(十二)c++调用minist训练的onnx模型

在实际使用过程中&#xff0c;使用python速度不够快&#xff0c;并且不太好嵌入到c程序中&#xff0c;因此可以把pytorch训练的模型转成onnx模型&#xff0c;然后使用opencv进行调用。 所需要用到的库有&#xff1a; opencv 1.完整的程序如下 import torch from torch impo…

零基础STM32单片机编程入门(十七)SPI总线详解及RC522-NFC刷卡模块实战含源码

文章目录 一.概要二.SPI总线基本概念1.SPI总线内部框图2.总体特征3.通讯时序 三.RC522介绍1.NFC基本介绍2.RC522模块基本特点3.RC522模块原理图4.RC522模块SPI通讯时序 四.RC522模块读卡实验五.CubeMX工程源代码下载六.小结 一.概要 SPI总线是由Motorola公司提出&#xff0c;是…

05_解封装和解码

1. 基本概念 容器就是一种文件格式&#xff0c;比如flv、mkv、mp4等。包含下面5种流以及文件头信息。 流是一种视频数据信息的传输方式&#xff0c;5种流&#xff1a;音频&#xff0c;视频&#xff0c;字幕&#xff0c;附件&#xff0c;数据。 包在ffmpeg中代表已经编码好的一…

FPGA实验3:D触发器设计

一、实验目的及要求 熟悉Quartus II 的 VHDL 文本设计简单时序电路的方法&#xff1b; 掌握时序电路的描述方法、波形仿真和测试&#xff0c;特别是时钟信号的特性。 二、实验原理 运用Quartus II 集成环境下的VHDL文本设计方法设计简单时序电路——D触发器&#xff0c;依据…

三相PWM整流器滞环电流控制仿真matlab simulink

1、内容简介 略 88-可以交流、咨询、答疑 2、内容说明 略 三相&#xff30;&#xff37;&#xff2d;整流器已广泛应用工业与电气控制领域电流控制技术决定着三相&#xff30;&#xff37;&#xff2d;整流器系统的控制性能。综合比 较了各种电流控制方法应用较多的滞环比较…

C++ 类和对象 构造函数(下)

一 初始化列表&#xff1a; 1.1 构造函数体赋值&#xff1a; 在C中&#xff0c;构造函数用于创建对象并赋予其初始值。通常&#xff0c;我们可以在构造函数体内对成员变量进行赋值&#xff1a; class Date { public:Date(int year, int month, int day) {_year year;_mont…

golang 解压带密码的zip包

目录 Zip文件详解ZIP 文件格式主要特性常用算法Zip格式结构图总览Zip文件结构详解数据区本地文件头文件数据文件描述 中央目录记录区&#xff08;核心目录记录区 &#xff09;中央目录记录尾部区 压缩包解压过程方式1 通过解析中央目录区来解压方式2 通过读取本地文件头来解压两…