当前位置: 首页 > news >正文

数据结构(三)——双向链表的介绍以及实现

前言

前面两期数据结构的文章我们介绍了顺序表和单向链表,那么本篇博文我们将来了解双向链表,作为最好用的一种链表,双向链表有什么特殊之处呢,接下来就让我们一起了解一下吧。

下面是前两篇数据结构的文章:

数据结构(一)——顺序表的介绍

 数据结构(二)——链表的介绍以及单链表的实现

双向链表的概念

什么是双向链表呢?实际上,双向链表全称是带头双向循环链表,是一种最复杂但最好用的一种链表形式,它不同于单项链表,它拥有一个头节点,正是由于头节点的存在,弥补了单项链表尾插的不便,同时它拥有两个指针,分别指向前驱和后继,从而方便链表进行数据的挪动等操作。

双向链表的实现

1.定义

目录

前言

双向链表的概念

双向链表的实现

1.定义

2.双向链表接口定义

1.初始化

2.销毁

4.打印

5.尾插/尾删

6.头插/头删

7.任意位置插入/删除

8.查找

小结


typedef int LTDataType;
typedef struct LTNode
{struct LTNode* next;struct LTNode* prev;LTDataType x;
}LTNode;

双向链表的定义如上所示,我们发现,在这个结构体内,我们定义了两个结构体指针,分别指向结构体变量(双向链表)的前驱和后继节点,后续我们将通过这个结构体指针完成链表的头插,尾插等一系列操作。

2.双向链表接口定义

接下来我们将定义一系列链表的接口,通过这些函数,我们将完成链表的初始化、销毁、头插、尾插、头删、尾删、打印等一系列操作。

//链表的初始化
void LTInit(LTNode** pphead);
LTNode* LTInit();
//链表的销毁
void LTDestroy(LTNode* phead);
//链表的打印
void LTPrint(LTNode* phead);//链表的尾插
void LTPushBack(LTNode* phead,LTDataType x);
//链表的头插
void LTPushFront(LTNode* phead, LTDataType x);//链表的尾删
void LTPopBack(LTNode* phead);
//链表的头删
void LTPopFront(LTNode* phead);//在指定位置之后插入
void LTInsert(LTNode* pos, LTNode* phead);
//删除指定位置的节点
void LTErase(LTNode* pos);//查找一个值返回相应节点
LTNode* LTFind(LTNode* phead, LTDataType x);

 从上述节点的定义我们发现,不同于单向链表,我们传的大多是一级指针,这是为什么呢?原因在于,双向链表多了一个哨兵位,当链表中只有哨兵位时我们称之为空链表,即哨兵位是不能删除的,而我们大多数操作是不会改变哨兵位的,所以只需要传一级指针。

1.初始化

我们看到,我们给出了两种链表的初始化方式一种是传二级指针,一种则是通过返回值接收,下面我们将实现代码如下:

void LTInit(LTNode** pphead)
{*pphead = (LTDataType*)malloc(sizeof(LTNode));if (*pphead == NULL){perror("malloc fail!");exit(1);}(*pphead)->a = -1;(*pphead)->next = (*pphead)->prev = *pphead;
}

首先是二级指针初始化,我们看到,对二级指针解引用我们可以拿到这个地址,然后malloc一块空间,最后让前驱节点和后继节点都指向自己,这样我们就完成了链表的初始化。但是细心观察我们发现,这一段代码的逻辑是先开一块新的节点,然后再完成初始化,后续我们对链表的插入修改还要用到这一段逻辑,因此我们可以把这个逻辑写成申请节点的函数,这样既减少了代码量,还提高了可读性,所以就有了第二种初始化方式,代码如下:

LTNode* LTBuyNode(LTDataType x)
{//申请一个新的节点LTNode* newnode = (LTDataType*)malloc(sizeof(LTNode));if (newnode == NULL){perror("malloc fail!");exit(1);}newnode->a = x;//让新节点的next prev指针指向自己newnode->next = newnode->prev = newnode;return newnode;
}
LTNode* LTInit()
{LTNode* phead = LTBuyNode(-1);return phead;
}

2.销毁

由于链表的每个节点不是连续的,所以我们需要循环销毁每一个节点,有了这个逻辑,我们就可以写下如下代码:

void LTDestroy(LTNode* phead)
{assert(phead);//pcur指向第一个节点LTNode* pcur = phead->next;while (pcur != phead){//记录下一个节点LTNode* next = pcur->next;//销毁该节点free(pcur);//让pcur指向下一个节点pcur = next;}//销毁哨兵位头节点free(phead);phead = NULL;
}

4.打印

与链表销毁类似,打印每个节点的值都是先找到该节点,然后再将该节点的值打印出来,因此我们可以写下如下代码:

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

5.尾插/尾删

链表插入删除的关键是改变节点的指针,对于尾插而言,我们实际上是将该节点插入哨兵位的前面;对于尾删而言,我们实际上是删除的是哨兵位的前驱节点。因此改变指针指向实际上就是改变哨兵位前驱和后继节点的指向。代码如下:

//尾插
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;
}
//尾删
void LTPopBack(LTNode* phead)
{assert(phead);assert(phead->next != phead);LTNode* del = phead->next;LTNode* prve = phead->next->prev;phead->prev = prve;prve->next=phead;free(del);del = NULL;
}

6.头插/头删

//头插
void LTPushFront(LTNode* phead, LTDataType x)
{assert(phead);LTNode* newnode = LTBuyNode(x);newnode->next = phead->next;newnode->prev = phead;phead->next = newnode;phead->next->prev = newnode;
}
//头删
void LTPopFront(LTNode* phead)
{assert(phead);assert(phead->next != phead);LTNode* prve = phead->next;LTNode* del = phead->next->next;phead->next = del;del->prev = phead;free(prve);prve = NULL;
}

这里有个小技巧,在我们插入时,我们可以先让新节点的前驱和后继节点指向相应位置,改变其他指针的指向

7.任意位置插入/删除

我们在有了前面插入删除的逻辑之后,我们可以用相同的逻辑如法炮制,就可以很快写出任意位置插入删除的代码:

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);pos->next->prev = pos->prev;pos->prev->next = pos->next;free(pos);pos = NULL;
}

8.查找

查找的逻辑十分简单,通过遍历链表,找到相应元素,然后返回当前节点,如果没有找到,就返回空,代码如下:

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

小结

冰冻三尺非一日之寒,在之后的日子里我会持续更新与数据结构相关的内容,如果喜欢的话希望能够点赞关注加转发,您的支持就是对我最大的鼓励,同时也希望在学习路上的你能够坚持下去,半山腰很挤,我想去山顶看看。

相关文章:

数据结构(三)——双向链表的介绍以及实现

前言 前面两期数据结构的文章我们介绍了顺序表和单向链表,那么本篇博文我们将来了解双向链表,作为最好用的一种链表,双向链表有什么特殊之处呢,接下来就让我们一起了解一下吧。 下面是前两篇数据结构的文章: 数据结…...

Webpack开发模式及处理样式资源

一、开发模式介绍 开发模式顾名思义就是我们开发代码时使用的模式。 这个模式下我们主要做两件事: 编译代码,使浏览器能识别运行 开发时我们有样式资源、字体图标、图片资源、html 资源等,webpack 默认都不能处理这些资源,所以我…...

leetcode--设计链表

707.设计链表 你可以选择使用单链表或者双链表,设计并实现自己的链表。 单链表中的节点应该具备两个属性:val 和 next 。val 是当前节点的值,next 是指向下一个节点的指针/引用。 如果是双向链表,则还需要属性 prev 以指示链表中的…...

【MySQL】:数据库操作

MySQL 数据库基础理论 2.1 数据库系统概述 介绍数据库系统的基本概念、发展历程、分类及 MySQL 在其中的地位与特点。 2.2 MySQL 数据库体系结构 解析 MySQL 的整体架构,包括服务器层与存储引擎层的功能与交互机制,重点探讨 InnoDB、MyISAM 等存…...

刷蓝桥杯历年考题(更新至15届~)

第十五届 CA组省赛 AcWing5980.训练士兵 方法一:树状数组:O(nlogn) self-complete /*先枚举组团,后分析每个士兵,有一个特点,组团费用是固定的,那当然是让所有士兵一块训练,训练完的士兵也不会有损失当还…...

AI与BI的火花:大语言模型如何重塑商业智能的未来

大家好,我是独孤风。 在当今这个数据驱动的时代,企业对于信息的需求如同对于氧气的需求一般至关重要。商业智能(BI)作为企业获取、分析和呈现数据的关键工具,正在经历一场深刻的变革,而这一变革的催化剂正是…...

Qt 详解QtNFC 读写模式

文章目录 Qt NFC 读写模式详解1. NFC 读写模式简介1.1 什么是 NFC 读写模式?主要功能: 1.2 常见应用场景 2. Qt NFC 读写模式原理3. 配置 QtNFC 模块4. NFC 读写操作实现4.1 NFC 标签读取代码示例功能解析 4.2 NFC 标签写入代码示例功能解析 5. 使用注意…...

增删改查文档

列表 : 列表包含 : 模糊查找 分页 列表jsp页面 : 一 :导入外部文件 (举例 : 用户点进来就可以看到菜单,这是预加载属于,使用文档就绪函数实现) 二 : body 上 ① : 文档就绪函数 ${ function() //获取条件查询的字段 //组装对象 //调用文档就绪函数 } ② : 封装ajax方…...

C语言蓝桥杯2023年省赛真题

文章目录 持续更新中...第一题题目描述输入格式输出格式样例输出提示 2 第二题题目描述 第三题题目描述输入格式输出格式样例输入样例输出 第四题题目描述输入格式输出格式样例输入样例输出提示 第四题题目描述输入格式输出格式样例输入样例输出提示 第五题题目描述输入格式输出…...

Python迭代器-大数据量的处理

一 生成器的实际使用(大量数据的导出) #分批导出数据然后分批写入excel import pandas as pd import openpyxl from openpyxl.utils.dataframe import dataframe_to_rowsdef execute_query(query):# 假设这是执行 SQL 查询的函数# 返回查询结果passdef …...

自动化包括态交互与感交互,而智能化包括势交互与知交互

“自动化包括态交互与感交互,而智能化包括势交互与知交互”交互框架将交互过程划分为不同类型,有助于更清晰地理解自动化和智能化的本质及其在未来agent应用中的差异与联系。 1. 自动化:态交互与感交互 自动化主要关注的是高效、无差错地执行…...

VideoBooth: Diffusion-based Video Generation with Image Prompts

VideoBooth: Diffusion-based Video Generation with Image Prompts 概括 文章提出了一个视频生成模型VideoBooth,输入一张图片和一个文本提示词,即可输出保持图片中物体且符合文本提示词要求的视频。 方法 粗-细两阶段设计:1)…...

模拟简单的iOT工作流

没有实际接触过iOT的流程,应该实际使用比这个接口返回要复杂,只是演示~希望能参与实际的接口接入,而不是只展示个假数据。 启动RabbitQ 使用的是3.8.5 启动命令 RabbitMQ Service - start RabbitMQ Command Prompt rabbitmqctl start_app …...

C++学习0.2: RAII

引用: 【代码质量】RAII在C编程中的必要性_raii 在c中的重要性-CSDN博客 C RAII典型应用之lock_guard和unique_lock模板_raii lock-CSDN博客 前言: 常用的线程间同步/通信(IPC)方式有锁(互斥锁、读写锁、自旋锁)、…...

k8s,进一步理解Pod

比如,凡是调度、网络、存储,以及安全相关的属性,基本上是Pod 级别的。 这些属性的共同特征是,它们描述的是“机器”这个整体,而不是里面运行的“程序”。比如,配置这个“机器”的网卡(即&#…...

MFC图形函数学习13——在图形界面输出文字

本篇是图形函数学习的最后一篇,相关内容暂告一段落。 在图形界面输出文字,涉及文字字体、大小、颜色、背景、显示等问题,完成这些需要系列函数的支持。下面做简要介绍。 一、输出文本函数 原型:virtual BOOL te…...

【Canvas与雷达】点鼠标可暂停金边蓝屏雷达显示屏

【成图】 【代码】 <!DOCTYPE html> <html lang"utf-8"> <meta http-equiv"Content-Type" content"text/html; charsetutf-8"/> <head><title>点鼠标可暂停金边蓝屏雷达显示屏 Draft1</title><style typ…...

React第十二节组件之间通讯之发布订阅模式(使用pubsub-js插件)

组件之间通讯常用方案 1、通过props 2、通过context 3、通过发布订阅模式 4、通过Redux 后面会有专栏介绍 1、安装 pubsub-js 插件 yarn add pubsub-js 常用的事件 a、发布事件&#xff1a;传入一个自定义事件名称&#xff08;name&#xff09;&#xff0c;以及要发布的消息内…...

Vue3安装 运行教程

本文是综合了所有vue安装教程而成 更细化 更简略 希望对各位读者有所帮助&#xff01; Vue安装 1. Vue-cli脚手架安装 安装vue的方式有很多 我们这里选择npm方式安装vue npm方式 npm方式安装vue&#xff0c;详细介绍见下文。 1.node.js安装和配置 安装npm 需要安装note.js&…...

MySQL:约束constraint

约束就是表中数据的限制条件. 表在设计的时候加入约束的目的是为了保证表中记录的完整性和有效性&#xff0c;如用户表有些列的值&#xff08;手机号&#xff09;不能为空&#xff0c;有些列的值&#xff08;身份证号&#xff09;不能重复。 主键约束(primary key) PK MySQL主…...

在快马平台快速搭建transformer文本分类原型,验证注意力机制

在深度学习领域&#xff0c;transformer架构已经成为自然语言处理&#xff08;NLP&#xff09;任务的核心工具。最近我在尝试搭建一个基于transformer的文本分类模型原型&#xff0c;用来验证注意力机制的效果。整个过程比想象中顺利得多&#xff0c;尤其是在InsCode(快马)平台…...

BilibiliDown:基于Java的B站视频下载技术方案与实现解析

BilibiliDown&#xff1a;基于Java的B站视频下载技术方案与实现解析 【免费下载链接】BilibiliDown (GUI-多平台支持) B站 哔哩哔哩 视频下载器。支持稍后再看、收藏夹、UP主视频批量下载|Bilibili Video Downloader &#x1f633; 项目地址: https://gitcode.com/gh_mirrors…...

保姆级教程:用Kalibr搞定Realsense D455相机+IMU联合标定(含常见报错解决)

深度视觉传感器多模态标定实战指南&#xff1a;从Realsense D455到SLAM算法优化 在机器人感知与自主导航领域&#xff0c;视觉-惯性系统的精确标定是构建可靠SLAM/VIO算法的基石。本文将以Intel Realsense D455这款集成RGB-D相机与IMU的旗舰设备为例&#xff0c;系统讲解从单目…...

别再死记硬背了!用一张图+代码示例,彻底搞懂蓝牙BLE配对的6种SMP流程

蓝牙BLE安全配对实战图解&#xff1a;6种SMP流程与核心算法拆解 每次看到蓝牙协议栈里那些晦涩的安全管理协议&#xff08;SMP&#xff09;文档就头疼&#xff1f;别担心&#xff0c;今天我们用工程师的思维来重新解构这个"安全黑匣子"。扔掉那些让人昏昏欲睡的文字…...

Qwen3-14B私有部署商业应用:替代SaaS服务降本提效的真实测算

Qwen3-14B私有部署商业应用&#xff1a;替代SaaS服务降本提效的真实测算 1. 私有部署的商业价值 在当今企业数字化转型浪潮中&#xff0c;大语言模型的应用已经成为提升效率的关键工具。然而&#xff0c;依赖第三方SaaS服务不仅成本高昂&#xff0c;还存在数据安全和响应速度…...

Graphormer多场景教程:学术论文配图生成、课程教学演示、项目原型开发

Graphormer多场景教程&#xff1a;学术论文配图生成、课程教学演示、项目原型开发 1. 认识Graphormer模型 Graphormer是一种基于纯Transformer架构的图神经网络&#xff0c;专门为分子图&#xff08;原子-键结构&#xff09;的全局结构建模与属性预测而设计。这个模型在OGB、…...

Qwen2.5-14B-Instruct开源大模型应用:像素剧本圣殿实现剧本动作/对白/旁白自动分段

Qwen2.5-14B-Instruct开源大模型应用&#xff1a;像素剧本圣殿实现剧本动作/对白/旁白自动分段 1. 项目概述 像素剧本圣殿&#xff08;Pixel Script Temple&#xff09;是一款基于Qwen2.5-14B-Instruct深度微调的专业剧本创作工具。它将先进的AI推理能力与独特的8-Bit复古美学…...

解决SlowFast环境配置中的‘No module named torch._six’等疑难杂症:从修改压缩包到调整import路径

SlowFast环境配置深度排障指南&#xff1a;从源码修改到路径调整的完整解决方案 在视频理解领域&#xff0c;SlowFast作为Facebook Research开源的优秀框架&#xff0c;凭借其双路径网络设计在动作识别任务中表现出色。然而&#xff0c;许多开发者在环境配置阶段就会遭遇各种&q…...

丹青幻境效果展示:宣纸底纹UI下生成图像与界面美学统一性视觉报告

丹青幻境效果展示&#xff1a;宣纸底纹UI下生成图像与界面美学统一性视觉报告 1. 设计理念与视觉定位 丹青幻境的设计理念源于传统东方美学与现代数字艺术的完美融合。这款基于Z-Image架构打造的数字艺术创作工具&#xff0c;彻底摒弃了传统AI工具冰冷的技术感&#xff0c;将…...

Phi-3-mini-4k-instruct-gguf效果展示:温度0.0下100%一致性的制度类文本生成

Phi-3-mini-4k-instruct-gguf效果展示&#xff1a;温度0.0下100%一致性的制度类文本生成 1. 模型介绍与特点 Phi-3-mini-4k-instruct-gguf是微软推出的轻量级文本生成模型&#xff0c;属于Phi-3系列中的GGUF版本。这个模型特别适合需要稳定、一致输出的场景&#xff0c;尤其是…...