数据结构 【双向哨兵位循环链表】

        链表的结构分为8中,其实搞懂了单链表和双向哨兵位循环链表,这部分的知识也就掌握的差不多了。双向哨兵位循环链表的结构如下:        

        下面我从0构建一个双向哨兵位循环链表。

1、准备工作 

        构建节点结构体,双向循环链表的每一个节点内需要有两个指针变量,一个指针变量指向前一个节点,另一个指针变量指向后一个节点。这里将指向前一个节点的指针变量命名为prev,指向后一个节点的指针变量命名为next,那么节点的结构可以定义为:

typedef int LDataType;typedef struct LNode
{LDataType data;struct LNode* prev;struct LNode* next;}LNode;

       存储的数据类型为LDataType类型。

2、链表的初始化与销毁

        在链表的初始化部分,我们需要创建一个哨兵位节点用于后面链表的搭建。它的结构可以定义为下面的形式:

        哨兵位节点的prev和next指针均指向自己本身从而形成循环,代码可以写成如下的形式:

LNode* LNodeCreat()
{LNode* pHead = (LNode*)malloc(sizeof(LNode));if (pHead == NULL){perror("malloc fail");return NULL;}pHead->next = pHead->prev = pHead;return pHead;
}

         链表的销毁部分应该考虑到后续插入来的节点,应该遍历整个链表将每一个节点进行释放并置空,最后再销毁哨兵位的头节点。

void LNodeDestroy(LNode* pHead)
{assert(pHead);LNode* cur = pHead->next;while (cur != pHead){LNode* next = cur->next;free(cur);cur = next;}free(pHead);
}

3、链表的末尾插入

        这里将创建一个新的节点封装成一个函数,为了方便后面继续使用。创建的新节点的prev和next均指向NULL,新节点内包含的数据为用户需要插入的数据val.下面是创建节点的函数实现:

LNode* BuyNewNode(LDataType val)
{LNode* newNode = (LNode*)malloc(sizeof(LNode));if (newNode == NULL){perror("BuyNewNode malloc fail");return NULL;}newNode->prev = newNode->next = NULL;newNode->data = val;
}

        创建的新节点的逻辑结构可以想象成下面的形式:

       我们知道,链表的尾插是需要找到尾节点的,在这里可以很方便的找到链表的尾节点tail,因为哨兵位的头节点指向的就是链表的尾节点,所以说不管原链表中是否存在节点在这里均可视为一种情况,插入的逻辑图为:

 

        代码的实现如下:

void LNodePushBack(LNode* pHead, LDataType val)
{assert(pHead);LNode* newNode = BuyNewNode(val);LNode* tail = pHead->prev;tail->next = newNode;newNode->prev = tail;pHead->prev = newNode;newNode->next = pHead;
}

4、链表的打印

        上面实现了链表的尾插,为了验证上述代码的正确性,这里先来写链表的打印,以便及时发现存在的问题。这里的思想就是遍历整个链表打印节点中的数据。

void LNodePrint(LNode* pHead)
{assert(pHead);LNode* cur = pHead->next;while (cur != pHead){printf("%d <=>", cur->data);LNode* next = cur->next;cur = next;}printf("\n");}

        这里先来测试一下上述代码能否正常实现双向带头链表的功能。

//测试代码
void test()
{LNode* pList = LNodeCreat();LNodePushBack(pList, 1);LNodePushBack(pList, 2);LNodePushBack(pList, 3);LNodePushBack(pList, 4);LNodePrint(pList);LNodeDestroy(pList);
}int main()
{test();return 0;
}

        输出结果:

5、链表的头部插入

        头插的操作是十分简单的,在插入之前记录一下第一个节点的位置用于和新创建的节点链接。逻辑结构可以化成如下的形式:

        代码实现如下:

void LNodePushFront(LNode* pHead, LDataType val)
{assert(pHead);LNode* newNode = BuyNewNode(val);LNode* first = pHead->next;newNode->prev = pHead;pHead->next = newNode;newNode->next = first;first->prev = newNode;
}

 6、链表的尾删

        逻辑结构如下图所示:

        代码实现如下:

void LNodePopBack(LNode* pHead)
{assert(pHead);assert(pHead->prev != pHead);LNode* tail = pHead->prev;LNode* last = tail->prev;pHead->prev = last;last->next = pHead;free(tail);tail = NULL;
}

7、链表的头删

        逻辑结构如下:

        代码实现:

void LNodePopFront(LNode* pHead)
{assert(pHead);assert(pHead->prev != pHead);LNode* del = pHead->next;LNode* first = del->next;pHead->next = first;first->prev = pHead;free(del);del = NULL;
}

 8、链表节点的查找

        查找到目标节点,返回目标节点的地址,用于后续的操作。代码实现:

LNode* FindNode(LNode* pHead, LDataType val)
{assert(pHead);LNode* cur = pHead->next;while (cur != pHead){if (cur->data == val)return cur;cur = cur->next;}return NULL;
}

9、链表在pos前插入

        配合上面的查找函数进行使用,先查找到pos的位置,再进行插入操作。

        代码实现:

//在pos的前面插入
void LNodeInsert(LNode* pos, LDataType val)
{assert(pos);LNode* posPrev = pos->prev;LNode* newNode = BuyNewNode(val);posPrev->next = newNode;newNode->prev = posPrev;newNode->next = pos;pos->prev = newNode;}

10、删除pos位置的节点

        这个函数还是要配合查找函数进行使用,逻辑图如下:

        代码实现:

void LNodeErase(LNode* pos)
{assert(pos);LNode* posPrev = pos->prev;LNode* posNext = pos->next;posPrev->next = posNext;posNext->prev = posPrev;free(pos);pos = NULL;}

         至此,哨兵位头节点的双向循环链表已全部实现。希望能帮到读者。

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

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

相关文章

RabbitMQ的交换机总结

1.direct交换机 2.fanout交换机

MVC、EL、JSTL

1.MVC设计模式 三层&#xff1a; MVC&#xff1a; M&#xff08;Model&#xff09;模型&#xff1a;负责业务逻辑处理&#xff0c;数据库访问。 V&#xff08;View&#xff09;视图&#xff1a;负责与用户交互。 C&#xff08;Controller&#xff09;控制器&#xff1a;负责流程…

《Python基础》之函数的用法

一、简介 在 Python 中&#xff0c;函数是一段可重用的代码块&#xff0c;用于执行特定的任务。函数可以帮助你将代码模块化&#xff0c;提高代码的可读性和可维护性。 函数的用途 代码重用&#xff1a;通过函数&#xff0c;你可以将常用的代码块封装起来&#xff0c;避免重复…

《Shader入门精要》透明效果

代码以及实例图可以看github &#xff1a;zaizai77/Shader-Learn: 实现一些书里讲到的shader 在实时渲染中要实现透明效果&#xff0c;通常会在渲染模型时控制它的透明通道&#xff08;Alpha Channel&#xff09;​。当开启透明混合后&#xff0c;当一个物体被渲染到屏幕上时&…

PICO 获取设备号 SN码

Unity版本 2020.3.42f1c1PICO SDK版本PICO Unity Integration SDK-3.0.5-20241105Pico设备pico 4ultra 注意 此api暂时只测试企业版本 pico 4ultra 代码 using Unity.XR.PICO.TOBSupport;private void Awake() {bool result PXR_Enterprise.InitEnterpriseService();Debug.L…

D 型 GaN HEMT 在功率转换方面的优势

氮化镓 (GaN) 是一种 III-V 族宽带隙半导体&#xff0c;由于在用作横向高电子迁移率晶体管 (HEMT) 时具有卓越的材料和器件性能&#xff0c;因此在功率转换应用中得到越来越多的采用。 HEMT 中产生的高击穿电场 (3.3 MV/cm) 和高二维电子气 (2DEG) 载流子迁移率 (2,000 cm 2 /…

政安晨【零基础玩转各类开源AI项目】探索Cursor-AI Coder的应用实例

目录 Cusor的主要特点 Cusor实操 政安晨的个人主页&#xff1a;政安晨 欢迎 &#x1f44d;点赞✍评论⭐收藏 希望政安晨的博客能够对您有所裨益&#xff0c;如有不足之处&#xff0c;欢迎在评论区提出指正&#xff01; Cursor 是 Visual Studio Code 的一个分支。这使我们能够…

AI-agent矩阵营销:让品牌传播无处不在

矩阵营销是一种通过多平台联动构建品牌影响力的策略&#xff0c;而 AI-agent 技术让这一策略变得更加智能化。AI社媒引流王凭借其矩阵管理功能&#xff0c;帮助品牌在多个平台上实现深度覆盖与精准传播。 1. 矩阵营销的优势 品牌触达更广&#xff1a;多平台联动可以覆盖不同用…

1- 9 C 语言面向对象

面向对象的基本特性&#xff1a;封装&#xff0c;继承&#xff0c;多态 1.0 面向过程概念 当我们在编写程序时&#xff0c;通常采用以下步骤&#xff1a; 1. 将问题的解法分解成若干步骤 2. 使用函数分别实现这些步骤 3. 依次调用这些函数 这种编程风格的被称作 面向过程…

中国科学院大学研究生学术英语读写教程 Unit7 Materials Science TextA 原文和翻译

中国科学院大学研究生学术英语读写教程 Unit7 Materials Science TextA 原文和翻译 Why Is the Story of Materials Really the Story of Civilisation? 为什么材料的故事实际上就是文明的故事&#xff1f; Mark Miodownik 1 Everything is made of something. Take away co…

从零开始-VitePress 构建个人博客上传GitHub自动构建访问

从零开始-VitePress 构建个人博客上传GitHub自动构建访问 序言 VitePress 官网&#xff1a;VitePress 中文版 1. 什么是 VitePress VitePress 是一个静态站点生成器 (SSG)&#xff0c;专为构建快速、以内容为中心的站点而设计。简而言之&#xff0c;VitePress 获取用 Markdown…

TCP IP协议和网络安全

传输层的两个协议&#xff1a; 可靠传输 TCP 分段传输 建立对话&#xff08;消耗系统资源&#xff09; 丢失重传netstat -n 不可靠传输 UDP 一个数据包就能表达完整的意思或屏幕广播 应用层协议&#xff08;默认端口&#xff09;&#xff1a; httpTCP80 网页 ftpTCP21验证用户身…

mcu上一种利用伪随机数防止mac地址冲突的方法

一 前言 前段时间开发的一个带tcp功能的项目&#xff0c;出现了mac地址冲突的问题&#xff0c;领导让随机生成一个mac地址&#xff0c;因此研究了下随机数。 二 预研 1.硬随机数 硬随机数又叫真随机数&#xff0c;英文名称”true random number generator“,即通过硬件随机数…

英伟达发布 Edify 3D 生成模型,可以在两分钟内生成详细的、可用于生产的 3D 资源、生成有组织的 UV 贴图、4K 纹理和 PBR 材质。

英伟达发布 Edify 3D 生成模型&#xff0c;可以利用 Agents 自动判断提示词场景中需要的模型&#xff0c;生成后将他们组合为一个场景。 Edify 3D 可以在两分钟内生成详细的、可用于生产的 3D 资源、生成有组织的 UV 贴图、4K 纹理和 PBR 材质。 相关链接 论文&#xff1a;htt…

远程控制软件:探究云计算和人工智能的融合

在数字化时代&#xff0c;远程控制工具已成为我们工作与生活的重要部分。用户能够通过网络远程操作和管理另一台计算机&#xff0c;极大地提升了工作效率和便捷性。随着人工智能&#xff08;AI&#xff09;和云计算技术的飞速发展&#xff0c;远程控制工具也迎来了新的发展机遇…

腾讯云 AI 代码助手:产品研发过程的思考和方法论

一、文章摘要 本文将详细阐述 腾讯云 AI 代码助手的历史发展形态与产品整体架构&#xff0c;并从技术、研发方法论的角度分别阐述了产品的研发过程。 全文阅读约 5&#xff5e;8 分钟。 二、产品布局 AI 代码助手产品经历了三个时代的发展 第一代诸如 Eclipse、Jetbrains、V…

WebGIS技术汇总

WebGIS系统通常都围绕地图进行内容表达&#xff0c;但并不是有地图就一定是WebGIS&#xff0c;所以有必要讨论下基于Web的地图API分类及应用场景。 Web上的Map API主要分类如下几类&#xff1a; Charts&#xff1a;以D3.js&#xff0c;Echarts等为代表。LBS&#xff1a;以高德…

Oracle 深入学习 Part 9: Storage Structure and Relationships(存储结构与关系)

在数据库管理系统&#xff08;DBMS&#xff09;中&#xff0c;Segment&#xff08;段&#xff09;、Extent&#xff08;区块&#xff09; 和 Block&#xff08;块&#xff09; 是描述数据库物理存储结构的三个重要概念。这些概念帮助理解数据库是如何在磁盘等存储设备上组织和管…

C语言实例之10求0-200内的素数

1. 素数 素数&#xff08;Prime number&#xff09;&#xff0c;也叫质数&#xff0c;是指在大于 1 的自然数中&#xff0c;除了 1 和它自身外&#xff0c;不能被其他自然数整除的数。例如 2、3、5、7、11 等都是素数&#xff0c;而 4 能被 2 整除、6 能被 2 和 3 整除&#x…

使用Python和Pybind11调用C++程序(CMake编译)

目录 一、前言二、安装 pybind11三、编写C示例代码四、结合Pybind11和CMake编译C工程五、Python调用动态库六、参考 一、前言 跨语言调用能对不同计算机语言进行互补&#xff0c;本博客主要介绍如何实现Python调用C语言编写的函数。 实验环境&#xff1a; Linux gnuPython3.10…