《循环双向链表》(带哨兵位的头节点)

目录

​编辑

前言:

 关于双向循环带头链表:

 模拟实现双向循环带头链表:

1.typedef数据类型

2.打印链表

3.初始化链表:

4.创建节点

5.尾插

6.头插

7.尾删

8.头删

9.寻找节点

10.在节点前插入

11.删除指定节点

单链表和双链表的区别:

链表和顺序表的区别:

对于顺序表的优势:

顺序表的问题:

链表的优势:

链表的不足:

 

总结:


前言:

我们在上一篇blog中,对于单向链表且不带哨兵位的头节点有了初步的认识,具体内容可以参考以下blog:《单链表》的实现(不含哨兵位的单向链表)-CSDN博客

今天我们将要对于双向链表的进行模拟实现,由于代码实现起来简单,并且大部分与单链表内容相似,所以我在这里我会进行过多的赘述,我们只是来讲解双向带头链表是什么,剩下的内容将是全部的代码模拟。

 关于双向循环带头链表:

简化一点就是:

 

我们会在节点里面再多定义一个指针——prev,指向上一个节点,这样方便我们进行操作。

因为有了上一个节点的地址,我们不管在尾删还是头删都可以在不保存任何节点的情况下对该节点进行操作,因此我们就会简化一系列操作。

下面将是各个模块的详细代码:

 模拟实现双向循环带头链表:

1.typedef数据类型

typedef int LTDataType;typedef struct ListNode
{struct ListNode* next;struct ListNode* prev;LTDataType data;
}LTNode;

2.打印链表

void LTPrint(LTNode* phead)
{assert(phead);printf("哨兵位<=>");LTNode* cur = phead;while (cur!=phead){printf("%d<=>", cur->data);cur = cur->next;}printf("\n");
}

3.初始化链表:

LTNode* LTInit()
{return CreatLTNode(-1);
}

4.创建节点

LTNode* CreatLTNode(LTDataType x)
{LTNode* tmp = (LTNode*)malloc(sizeof(LTNode));if (tmp == NULL){perror("CreatNode -> malloc");exit(-1);}tmp->data = x;tmp->next = tmp;tmp->prev = tmp;return tmp;
}

5.尾插

void LTPushBack(LTNode* phead, LTDataType x)
{assert(phead);LTNode* newnode = CreatLTNode(x);if (phead->next == phead){phead->next = newnode;phead->prev = newnode;newnode->next = phead;newnode->prev = phead;}else{newnode->next = phead;phead->prev->next = newnode;newnode->prev = phead->prev;phead->prev = newnode;}
}

6.头插

void LTPushFront(LTNode* phead, LTDataType x)
{assert(phead);LTNode* newnode = CreatLTNode(x);phead->next = newnode;phead->prev = newnode;newnode->next = phead;newnode->prev = phead;
}

7.尾删

void LTPopBack(LTNode* phead)
{assert(phead);assert(phead->next != phead);LTNode* tail = phead->prev;tail->prev->next = phead;phead->prev = tail->prev;free(tail);tail = NULL;
}

8.头删

void LTPopFront(LTNode* phead)
{assert(phead);assert(phead->next != phead);LTNode* tmp = phead->next;phead->next = tmp->next;tmp->next->prev = phead;free(tmp);tmp = NULL;
}

9.寻找节点

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

10.在节点前插入

void ListInsert(LTNode* phead, LTNode* pos, LTDataType x)
{assert(pos);LTNode* newnode = CreatLTNode(x);newnode->prev = pos->prev;pos->prev->next = newnode;pos->prev = newnode;newnode->next = pos;pos->prev = newnode;
}

11.删除指定节点

void ListErase(LTNode* phead, LTNode* pos)
{assert(phead);pos->prev = pos->next;pos->next->prev = pos->prev;free(pos);
}

单链表和双链表的区别:

对于单链表我们想要进行找尾操作,十分困难,时间复杂度为O(N)

而在双向链表中,我们想要进行找尾操作则直接利用phead->prev就可以找到尾结点,时间复杂对为O(1)。

在我们以后的面试中,如果我们的面试官对我们提出,如何在10min之内创建一个链表。

这时候我们就可以信心满满的写出双线带头循环链表,并且只需要写出ListErase 与 ListInsert即可,因为这两个函数不需要进行分类讨论。

链表和顺序表的区别:

学习了链表与顺序表,这两种均属于线性表的产物,那么我们该如何选择呢?

对于顺序表的优势:

1.支持下标的随机访问。

2.CPU高速缓存命中率较高

顺序表的问题:

1.头部或中间插入删除效率低,需要挪动数据O(N)

2.空间不够需要扩容,扩容一定要消耗,且可能存在一定的空间浪费。

3.只适合尾插尾删。

链表的优势:

1.任意位置插入删除都是O(1)。

2.按需申请释放,合理利用空间,不存在浪费

链表的不足:

  1. 随机访问性差:链表中的元素并不存储在连续的内存位置上,因此要访问链表中的任意元素需要遍历整个链表,时间复杂度为O(n)。

  2. 存储空间浪费:链表中每个节点需要额外的一个指针来指向下一个节点,这样会使链表中存储的数据量比较少,占用的存储空间较大。

  3. 插入和删除操作的效率较高:虽然插入和删除操作都是链表的优点,但是在处理大量数据时,频繁的插入和删除操作会导致链表不断地重新分配内存空间,影响性能。

  4. 不支持随机访问:链表的遍历方式只能是从头节点开始,依次访问每个节点,不能直接访问某个节点的位置。

  5. 不利于缓存:由于链表中的元素在内存中的存储位置是随机的,所以在对链表进行遍历时,缓存命中率较低,效率较差

 

总结:

以上就是我们的双向带头链表的实习,以及对于顺序表和链表之间的区别,学习完后下来可以及时整理整理,在之后我们会对顺序表和链表进行综合运用,我们将在后面实现《栈》《队列》的模拟操作,也会对于相应的题目进行分析。

记住

“坐而言不如起而行”

Action speak louder than words!

 

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

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

相关文章

解析:什么是生成式AI?与其他类型的AI有何不同?

原创 | 文 BFT机器人 快速浏览一下头条新闻&#xff0c;你会发现生成式AI似乎无处不在。事实上&#xff0c;一些新闻标题甚至可能是通过生成式AI编写的&#xff0c;例如OpenAI旗下的ChatGPT&#xff0c;这个聊天机器人已经展现出了生成看起来像人类所写文本的惊人能力。 当人们…

maptalks三维地图网址

三维 地址: http://examples.maptalks.com/examples/cn/gltf/gltf-marker/shader

简朴博客系统测试报告

文章目录 一. 项目简介二. 测试概要三. 测试环境四. 测试执行概况及功能测试1. 手工测试1.1 手动测试用例编写1.2 执行的部分测试用例 2. 自动化测试Selenium2.1 编写测试用例2.2 自动化测试代码 3. 测试结果 五. 发现的问题 一. 项目简介 简朴博客系统是采用前后端分离的方式…

asp.net core mvc 之 依赖注入

一、视图中使用依赖注入 1、core目录下添加 LogHelperService.cs 类 public class LogHelperService{public void Add(){}public string Read(){return "日志读取";}} 2、Startup.cs 文件中 注入依赖注入 3、Views目录中 _ViewImports.cshtml 添加引用 4、视图使用…

Python每日一练@前言

Python每日一练前言 导读 人生苦短&#xff0c;我用Python 大家好&#xff0c;我是鹅不糊涂 欢迎大家来到Python每日一练 好处 加强编程能力: 每日一练可以帮助提升编程技能&#xff0c;通过解决各种编程问题和挑战&#xff0c;你能够不断锻炼自己的逻辑思维和解决问题的能力…

顺序表(数据结构与算法)

✅✅✅✅✅✅✅✅✅✅✅✅✅✅✅✅ ✨✨✨✨✨✨✨✨✨✨✨✨✨✨✨✨ &#x1f33f;&#x1f33f;&#x1f33f;&#x1f33f;&#x1f33f;&#x1f33f;&#x1f33f;&#x1f33f;&#x1f33f;&#x1f33f;&#x1f33f;&#x1f33f;&#x1f33f;&#x1f33f;&#x1…

2023.11.17 hadoop之HDFS进阶

目录 HDFS的机制 元数据简介 元数据存储流程:namenode 生成了多个edits文件和一个fsimage文件 edits和fsimage文件 SecondaryNameNode辅助NameNode的方式: HDFS的存储原理 写入数据原理: 发送写入请求,获取主节点同意,开始写入,写入完成 读取数据原理:发送读取请求,获取…

vim——“Linux”

各位CSDN的uu们好呀&#xff0c;今天&#xff0c;小雅兰的内容是Linux的开发工具——vim。下面&#xff0c;我们一起进入Linux的世界吧&#xff01;&#xff01;&#xff01; Linux编辑器-vim使用 vim的基本概念 vim的基本操作 vim正常模式命令集 vim末行模式命令集 vim操…

【Linux网络】从原理到实操,感受PXE无人值守自动化高效批量网络安装系统

一、PXE网络批量装机的介绍 1、常见的三种系统安装方式 2、回顾系统安装的过程&#xff0c;了解系统安装的必要条件 3、什么是pxe 4、搭建pxe的原理 5、Linux的光盘镜像中的isolinux中的相关文件学习 二、关于实现PXE无人值守装机的四大文件与五个软件的对应关系详解 5个…

使用 Redis BitMap 实现签到与查询历史签到以及签到统计功能(SpringBoot环境)

目录 一、前言二、Redis BitMap 位图原理2.1、BitMap 能解决什么2.2、BitMap 存储空间计算2.3、BitMap 存在问题 三、Redis BitMap 操作基本语法和原生实现签到3.1、基本语法3.2、Redis BitMap 实现签到操作指令 四、SpringBoot 使用 Redis BitMap 实现签到与统计功能4.1、代码…

python之 flask 框架(2)项目拆分的 执行逻辑

项目的结构图 app.py # 导入__init__.py 比较特殊 from APP import create_appapp create_app() if __name__ __main__:app.run(debugTrue)init.py # __inti__.py # 初始化文件&#xff0c;创建Flask应用 from flask import Flask from .views import bluedef create_ap…

PyTorch 实战之水果分类

当我们试图提高神经网络的准确性时&#xff0c;经常会遇到过拟合训练数据的情况。当我们运行测试数据的模型时&#xff0c;这会导致一个糟糕的预测。因此&#xff0c;我采取了一个数据集&#xff0c;并应用这些技术&#xff0c;不仅提高准确性&#xff0c;而且还处理过拟合的问…

基于IDEA 进行Maven依赖管理

1. 依赖管理概念 Maven 依赖管理是 Maven 软件中最重要的功能之一。Maven 的依赖管理能够帮助开发人员自动解决软件包依赖问题&#xff0c;使得开发人员能够轻松地将其他开发人员开发的模块或第三方框架集成到自己的应用程序或模块中&#xff0c;避免出现版本冲突和依赖缺失等…

一个用于操作Excel文件的.NET开源库

推荐一个高性能、跨平台的操作Excel文件的.NET开源库。 01 项目简介 ClosedXML是一个.NET第三方开源库&#xff0c;支持读取、操作和写入Excel 2007 (.xlsx&#xff0c; .xlsm)文件&#xff0c;是基于OpenXML封装的&#xff0c;让开发人员无需了解OpenXML API底层API&#xf…

go语言 | 图解字节青训营抖音(一)

前言 本文大致介绍了本人及本人所在小组为第五届字节跳动青训营后端专场大项目需求 —— 「实现一个极简版抖音」的部分实现细节。 需求 本届后端青训营大项目要求实现一个极简版抖音的后端服务&#xff0c;该后端服务通过 HTTP 协议向已被设计好的前端 App 传递数据&#xf…

识别代理IP:保障网络安全的重要一环

在互联网的世界中&#xff0c;代理服务器被广泛用于隐藏用户真实IP地址&#xff0c;带来了一些挑战&#xff0c;特别是在网络安全和欺诈检测方面。本文将探讨如何识别代理IP&#xff0c;以确保网络的安全性和可靠性。 1. 代理IP的背景与用途 代理服务器是位于用户和目标服务器…

吉林省土木建筑学会建筑电气分会及吉林省建筑电气情报网学术交流年会-安科瑞 蒋静

11月9-10日&#xff0c;吉林省土木建筑学会建筑电气分会及吉林省建筑电气情报网学术交流年会在吉林长春隆重举办。安科瑞电气股份有限公司作为智慧用电产品供应商受邀参会&#xff0c;为参会人士展示了安科瑞能源物联网云平台、电力运维云平台、智慧消防云平台、预付费管理云平…

k8s_base

应用程序在服务器上部署方式的演变,互联网发展到现在为止 应用程序在服务器上部署方式 历经了3个时代1. 传统部署 优点简单 缺点就是操作系统的资源是有限制的&#xff0c;比如说操作系统的磁盘&#xff0c;内存 比如说我8G&#xff0c;部署了3个应用程序&#xff0c;当有一天…

时序预测 | Python实现ConvLSTM卷积长短期记忆神经网络股票价格预测(Conv1D-LSTM)

时序预测 | Python实现ConvLSTM卷积长短期记忆神经网络股票价格预测(Conv1D-LSTM) 目录 时序预测 | Python实现ConvLSTM卷积长短期记忆神经网络股票价格预测(Conv1D-LSTM)预测效果基本介绍程序设计参考资料预测效果 基本介绍 时序预测 | Python实现ConvLSTM卷积长短期记忆神…

springMVC学习笔记-请求映射,参数绑定,响应,restful,响应状态码,springMVC拦截器

目录 概述 springMVC做了什么 springMVC与struts2区别 springMVC整个流程是一个单向闭环 springMVC具体的处理流程 springMVC的组成部分 请求映射 RequestMapping 用法 属性 1.value 2.method GET方式和POST方式 概述 HTTP给GET和POST做了哪些规定 GET方式&…