带头结点的双向循环链表

目录

带头结点的双向循环链表

 1.存储定义

2.结点的创建

3.结点的初始化

4.尾插结点

5.尾删结点

6.头插结点

7.头删结点 

8.查找并返回结点

9.在pos结点前插入结点

10.删除pos结点

11.打印链表

 12.销毁链表

13.头插结点2.0版

 14.尾插结点2.0版


前言:

当我们使用单链表时,想要去找到前面的前驱结点的时候,我们发现了受到限制,因为当时的结点只能去找后面的结点。

于是,就到了这里的带头结点的双向循环链表

如图:

首先给出  双向链表的定义:是在单链表的每个结点中,再设置一个指向其前驱结点的指针域。

那双向循环链表就是最后一个结点的next指向头结点,头结点的前驱指向尾结点。

带头结点的双向循环链表

 1.存储定义

typedef int CLDataType;typedef  struct ListNode
{CLDataType val;struct ListNode* next; //存放后面结点的指针struct ListNode* pre;  //存放前面结点的指针
}ListNode;

2.结点的创建

结点的创建,刚开始创建头结点我们一般会采用二级指针的方式,这里我们采用使用函数返回值的形式来避免野指针问题。

//创建链表
ListNode* CreateNode(CLDataType x) 
{//这里采用返回值的形式进行创建结点ListNode* newnode = (ListNode*)malloc(sizeof(ListNode));if (newnode == NULL) {perror("malloc");exit(-1);}newnode->val = x;newnode->pre = NULL;newnode->next = NULL;return newnode;
}

 如果是刚开始创建的头节点的话:

3.结点的初始化

这个时候就能体现带头结点双向循环链表的好处了

ListNode* InitNode() 
{ListNode* phead = CreateNode(-1);//让头节点的前驱和后继结点都指向自己phead->next = phead;	phead->pre = phead;return phead;
}

 因为修改了头结点,所以接着使用函数返回值的形式。

注意:初始化时,头结点的前驱和后继指针都指向自己。如图:

4.尾插结点

之前的单链表尾插需要遍历整个链表去找尾结点,但是,这里是带头结点的双向循环链表,只需去找头结点的前驱结点就找到了尾结点。

//尾插结点
void ListPushBack(ListNode* phead, CLDataType x) 
{assert(phead);ListNode* newnode = CreateNode(x);ListNode* tail = phead->pre;tail->next = newnode; //之前的尾结点的后继指针指向新结点newnode->pre = tail;  //新结点的前驱指向之前的尾结点newnode->next = phead; //新结点的后继指针指向头结点phead->pre = newnode;  //头结点的前驱指向

 当链表中只有一个头结点的时候,tail指向自己。

 再继续尾插时,仍是这样。

5.尾删结点

 一般都是先找到尾节结点,然后再去找到尾结点的前一个结点Pre。Pre指向头结点,头结点的前驱指向Pre。之后再删去尾节结。

//尾删结点
void ListPopBack(ListNode* phead)
{assert(phead);assert(phead->next != phead); //避免只有一个头结点ListNode* tail = phead->pre;ListNode* Pre = tail->pre; //用来记录尾结点的前一个结点Pre->next = phead;phead->pre = Pre;free(tail);tail = NULL;
}

注意:当只有一个头结点的情况时,说明已经没有链表结点。所以不能进行删除。进而这里采用,assert( phead->next != phead) 来避免此情况。

6.头插结点

和单链表的头插相似,用next指针记录头结点的下一个结点。先让新结点与next指针指向的结点建立连接,再与头结点建立连接。

//头插结点
void ListPushFront(ListNode* phead,CLDataType x) 
{assert(phead);ListNode* newnode = CreateNode(x);ListNode* next = phead->next; //记录头结点后的第一个结点newnode->next = next;next->pre = newnode;phead->next = newnode;newnode->pre = phead;
}

7.头删结点 

这里需要注意的是避免只有一个头结点的情况下,删除结点,这样使用同尾删结点一样的方法,通过断言 assert(phead->next != phead);

//头删结点
void ListPopFront(ListNode* phead) 
{assert(phead);assert(phead->next != phead);phead->next = phead->next->next;phead->next->next->pre = phead;
}

8.查找并返回结点

这里采用cur指针去遍历链表,找到就返回结点,没有找到就返回空。

//查找并返回结点
ListNode* ListFind(ListNode* phead, CLDataType x) 
{assert(phead);ListNode* cur = phead->next;while (cur != phead) {if (cur->val == x){return cur;}cur = cur->next;}return NULL;
}

9.在pos结点前插入结点

因为是带头结点的双向循环链表,这样就可以直接进行插入。

//在pos前面插入结点
void ListInsert(ListNode* pos, CLDataType x) 
{assert(pos);ListNode* newnode = CreateNode(x);//注意顺序//先让后驱建立连接newnode->next = pos;pos->pre->next = newnode;//再前驱建立连接newnode->pre = pos->pre;pos->pre = newnode;
}

10.删除pos结点

 修改pos的前驱和后继

//删除pos位置的结点
void ListErase(ListNode* pos) 
{assert(pos);pos->pre->next = pos->next;pos->next->pre = pos->pre;
}

11.打印链表

//打印链表
void ListPrint(ListNode* phead) 
{assert(phead);ListNode* cur = phead->next;printf("phead<==>");while (cur != phead) {printf("%d<==>", cur->val);cur = cur->next;}printf("\n");
}

 12.销毁链表

//销毁链表
void ListDestory(ListNode* phead) 
{assert(phead);ListNode* cur = phead->next;while (cur != phead){ListNode* next = cur->next;free(cur);cur = next;}free(phead);phead = NULL;
}

13.头插结点2.0版

这里我们借助 在pos结点前插入结点 的接口。

头插结点,那就是把pos结点变为 phead->next 的结点。这样结点的插入就模拟了头插结点,从而减少了一写代码的工作量。

//头插结点2.0
void ListushFront(ListNode* phead, CLDataType x)
{assert(phead);ListNode* newnode = CreateNode(x);ListNode* next = phead->next; //记录头结点后的第一个结点ListInsert(next,x); //这里的next相当于pos结点
}

例如  :在 1结点 前插入 结点25

 

 14.尾插结点2.0版

根据带头结点的双向循环链表的特性,再借助前面的 在pos结点前插入结点(ListInsert) 的接口

这时我们会想到的就是 尾插结点不是在尾结点的后面进行插入的吗,而ListInsert是在pos前面进行结点的插入。 这时就采用  在头结点的前进行插入结点,从而达到尾插结点的特性。

//尾插结点
void ListPushBack(ListNode* phead, CLDataType x)
{assert(phead);ListNode* newnode = CreateNode(x);ListInsert(phead,x);
}

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

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

相关文章

深入探究Python中的JSON、Pickle和Shelve模块:特性与区别

更多资料获取 &#x1f4da; 个人网站&#xff1a;ipengtao.com 在Python中&#xff0c;处理数据序列化和持久化是极其重要的。JSON、Pickle和Shelve是三种常用的模块&#xff0c;它们提供了不同的方法来处理数据的序列化和持久化。本文将深入研究这三个模块&#xff0c;探讨它…

VBA_MF系列技术资料1-232

MF系列VBA技术资料 为了让广大学员在VBA编程中有切实可行的思路及有效的提高自己的编程技巧&#xff0c;我参考大量的资料&#xff0c;并结合自己的经验总结了这份MF系列VBA技术综合资料&#xff0c;而且开放源码&#xff08;MF04除外&#xff09;&#xff0c;其中MF01-04属于定…

【高效开发工具系列】Hutool DateUtil工具类

&#x1f49d;&#x1f49d;&#x1f49d;欢迎来到我的博客&#xff0c;很高兴能够在这里和您见面&#xff01;希望您在这里可以感受到一份轻松愉快的氛围&#xff0c;不仅可以获得有趣的内容和知识&#xff0c;也可以畅所欲言、分享您的想法和见解。 推荐:kwan 的首页,持续学…

Python+Requests模块获取响应内容

Requests模块获取响应内容 响应包括响应行、响应头、响应正文内容&#xff0c;这些返回的响应信息都可以通过Requests模块获取。这些 获取到的响应内容也是接口测试执行得到的实际结果。 获取响应行 获取响应头 获取其它响应信息 代码示例&#xff1a; # 导入requests模块…

算法通关村第十四关-青铜挑战认识堆

大家好我是苏麟 , 今天带大家认识认识堆 . 堆 堆是将一组数据按照完全二叉树的存储顺序&#xff0c;将数据存储在一个一维数组中的结构。 堆有两种结构&#xff0c;一种称为大顶堆&#xff0c;一种称为小顶堆 : 大顶堆 大顶堆的任何一个父节点的值&#xff0c;都大于或等于…

前端下拉框select标签的插件——select2.js

本文采用的是select2 版本:Select2 4.0.6-rc.1。 可以兼容IE8及以上。亲测过。 官网:Getting Started | Select2 - The jQuery replacement for select boxes 一、认识select2.js 1、使用插件,首先要引入别人的插件了,你可以选择离线(无网络)或者在线引用的(如果有网…

ios 逆向分分析,某业帮逆向算法(一)

用到工具: 爱思助手CrackerXL(砸壳软件)越狱手机ida反汇编软件分析login 的sign 签名算法中自己写算法 已知我们32位,我们不妨猜测是md5 ,那我们试图使用CC_MD5 ,这个是ios 中的标准库, 我们使用frida-trace 注入hook一下,看看有没有 经过 是经过了这个函数,密码也是…

新建的springboot项目中application.xml没有绿色小叶子(不可用)

经常有朋友会遇到新建了一个springboot项目&#xff0c;发现为啥我创建的application.xml配置文件不是绿色的&#xff1f;&#xff1f;&#xff1f; 下面教大家如何解决&#xff0c;这也是博主在做测试的时候遇到的&#xff1a; 将当前位置application.xml删掉&#xff0c;重新…

在Spring Boot中使用@Async实现一个异步调用

在使用异步注解之前&#xff0c;我们需要先了解&#xff0c;什么是异步调用&#xff1f; 异步调用对应的事同步调用&#xff0c;同步调用是值程序按照我们定义的顺序依次执行&#xff0c;每一行程序都必须等待上一行的程序执行完成之后才执行&#xff0c;而异步是指在顺序执行…

YOLOv8 第Y7周 水果识别

1.创建文件夹&#xff1a; YOLOv8开源地址 -- ultralytics-main文件下载链接&#xff1a;GitHub - ultralytics/ultralytics: NEW - YOLOv8 &#x1f680; in PyTorch > ONNX > OpenVINO > CoreML > TFLite 其余文件由代码生成。 数据集下载地址&#xff1a;Frui…

使用NVM管理多个版本的node.js

1、nvm介绍&#xff1a; nvm全英文也叫node.js version management&#xff0c;是一个nodejs的版本管理工具。nvm是node.js版本管理工具&#xff0c;为了解决node.js各种版本存在不兼容现象可以通过它可以安装和切换不同版本的node.js 2、下载nvm地址&#xff1a; https://d…

Mybatis如何执行批量操作

文章目录 Mybatis如何执行批量操作使用foreach标签 使用ExecutorType.BATCH如何获取生成的主键 Mybatis如何执行批量操作 使用foreach标签 foreach的主要用在构建in条件中&#xff0c;它可以在SQL语句中进行迭代一个集合。foreach标签的属性主要有item&#xff0c;index&…

iPhone苹果手机如何将词令网页添加到苹果iPhone手机桌面快捷打开?

iPhone苹果手机如何将词令网页添加到苹果iPhone手机桌面快捷打开&#xff1f; 1、在iPhone苹果手机上找到「Safari浏览器」,并点击打开&#xff1b; 2、打开Safari浏览器后&#xff0c;输入词令官方网站地址&#xff1a;ciling.cn ; 3、打开词令官网后&#xff0c;点击Safari…

Maven的配置亲测有效

文章目录 前言一、maven网址二、操作步骤三.配置环境变量四.配置本地仓库五.找到mirror 和配置JDK六.胜利七.提醒⏰;总结 前言 &#xff08;我讲一下什么是maven&#xff0c;不想看跳到下一步就行了&#xff0c;也没必要看&#xff09; Maven&#xff08;Apache Maven&#x…

使用策略模式彻底消除if-else

文章目录 使用策略模式彻底消除if-else1. 场景描述2. if-else方式3. 策略模式 使用策略模式彻底消除if-else 如果一个对象有很多的行为&#xff0c;如果不用恰当的模式&#xff0c;这些行为就只好使用多重的条件选择语句来实现&#xff0c;这样会显得代码逻辑很臃肿&#xff0c…

【广州华锐视点】机械零件拆装VR仿真教学系统

随着科技的不断发展&#xff0c;虚拟现实&#xff08;VR&#xff09;技术已经逐渐走进我们的生活。在教育领域&#xff0c;VR技术的应用也日益广泛&#xff0c;为学生提供了更加生动、直观的学习体验。广州华锐视点开发的机械零件拆装VR仿真教学系统作为一种新兴的教学方式&…

Vision Pro即将量产之际,苹果还是放不下中国供应链

发布半年后&#xff0c;苹果口中的“革命性新产品”Vision Pro终于有了新消息。 图源&#xff1a;苹果 根据多家消费电子供应链公司核心人士消息&#xff0c;苹果计划于12月正式量产Vision Pro&#xff0c;首批备货40万台左右&#xff0c;2024年的销量目标为100万台&#xff0…

网络篇---第九篇

系列文章目录 文章目录 系列文章目录前言一、说说TCP/IP四层网络模型二、说说域名解析详细过程?三、 IP 地址分为几类,每类都代表什么,私网是哪些?四、说说TCP 如何保证可靠性的?前言 前些天发现了一个巨牛的人工智能学习网站,通俗易懂,风趣幽默,忍不住分享一下给大家…

W1R3S

信息收集 主机发现 利用nmap -sn参数进行主机发现&#xff0c;该参数是当发现存活主机的时候&#xff0c;不进行端口的探测&#xff0c;其中的n并不是network的含义&#xff0c;而是not port的意思&#xff01; nmap -sn 192.168.217.0/24 -oA ./hosts 这种方式和arp-scan -…

JOSEF约瑟 DY-34 型电压继电器,15-30V 柜内安装,板前接线

DY-30系列电压继电器 DY-32电压继电器&#xff1b; DY-36电压继电器&#xff1b; DY-33电压继电器&#xff1b; DY-37电压继电器&#xff1b; DY-34电压继电器&#xff1b; DY-38电压继电器&#xff1b; DY-31电压继电器&#xff1b; DY-35电压继电器&#xff1b; DY-32/60C电压…