带头结点的双向循环链表

目录

带头结点的双向循环链表

 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;探讨它…

conda和pip常用命令整理

文章目录 一、conda常用指令1. 更新2 .环境管理3. 包管理 二、pip常用命令1. 常用命令2. 国内镜像 一、conda常用指令 1. 更新 conda --version 或 conda -V #查看conda版本 conda update conda # 基本升级 conda update anaconda # 大的升级 conda upd…

VBA_MF系列技术资料1-232

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

动态规划:多重背包问题-一维滚动数组解法

题目描述 你是一名宇航员&#xff0c;即将前往一个遥远的行星。在这个行星上&#xff0c;有许多不同类型的矿石资源&#xff0c;每种矿石都有不同的重要性和价值。你需要选择哪些矿石带回地球&#xff0c;但你的宇航舱有一定的容量限制。 给定一个宇航舱&#xff0c;最大容量…

02_学习使用javax_ws_rs_下载文件

文章目录 1 前言2 Maven 依赖3 下载接口4 如何返回文件&#xff1f;5 感谢 1 前言 专栏上一篇&#xff0c;写了如何使用 javax.ws.rs 上传文件&#xff0c;那么必然的&#xff0c;我们得再学习学习如何下载文件&#x1f600; 2 Maven 依赖 这个就不赘述了&#xff0c;和上一篇…

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

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

网络爬虫与指纹浏览器:解析指纹浏览器对网络爬虫的作用

网络爬虫在信息搜集、数据挖掘等领域起着重要作用。然而&#xff0c;传统爬虫往往面临被目标网站封禁的风险。本文将介绍指纹浏览器对网络爬虫的作用&#xff0c;以及指纹浏览器如何帮助爬虫降低封禁风险。 网络爬虫面临的挑战 网络爬虫是一种自动化程序&#xff0c;用于从互联…

Python+Requests模块获取响应内容

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

华为OD机试真题-分割均衡字符串-2023年OD统一考试(C卷)

题目描述&#xff1a; 均衡串定义&#xff1a;字符串只包含两种字符&#xff0c;且两种字符的个数相同。 给定一个均衡字符串&#xff0c;请给出可分割成新的均衡子串的最大个数。 约定字符串中只包含大写的X和Y两种字符。 输入描述&#xff1a; 均衡串&#xff1a;XXYYXY 字符…

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

大家好我是苏麟 , 今天带大家认识认识堆 . 堆 堆是将一组数据按照完全二叉树的存储顺序&#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;重新…

Java,Stream API的使用

Stream是数据渠道&#xff0c;用于操作数据源&#xff08;集合、数组等&#xff09;所生成的元素序列。 Stream和Collection集合的区别&#xff1a;Collection是一种静态的内存数据结构&#xff0c;讲的是数据&#xff0c;而Stream是有关计算的&#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…