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

目录

​编辑

前言:

 关于双向循环带头链表:

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

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.hqwc.cn/news/190732.html

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

相关文章

c语言免杀火绒

文章目录 前记c加载器补充知识 前记 pyinstaller pyinstaller目前已经被杀疯了&#xff0c;简单打包一个hello a"hello" print(a)#pyinstaller -F -w b.py -n HipsMain.exe考虑Nuitka pip uninstall nuitka pip install nuitka pip install nuitka1.8.5 这里最新…

Python使用大连理工情感本体提取文本的情感倾向

import pandas as pd # 导入词典 df pd.read_excel(Sentiment_dictionary\大连理工情感词汇本体\情感词汇本体.xlsx) # 我们暂时只使用 [词语,词性种类,词义数,词义序号,情感分类,强度,极性] df df[[词语, 词性种类, 词义数, 词义序号, 情感分类, 强度, 极性]] df.head()# 按…

scss的高级用法——循环

周末愉快呀&#xff01;一起来学一点简单但非常有用的css小知识。 最近在一个项目中看到以下css class写法&#xff1a; 了解过tailwind css或者unocss的都知道&#xff0c;从命名就可以看出有以下样式&#xff1a; font-size: 30pxmargin-left: 5px;margin-top: 10px; 于是…

【字符编码系列一】ASCII编码是什么?

介绍 ASCII 编码于 1967 年第一次发布&#xff0c;最后一次更新是在 1986 年&#xff0c;迄今为止共收录了 128 个字符&#xff0c;包含了基本的拉丁字母&#xff08;英文字母&#xff09;、阿拉伯数字&#xff08;也就是 1234567890&#xff09;、标点符号&#xff08;,.!等&…

Java Fasn 带您谈谈——开源、闭源

✅作者简介&#xff1a;2022年博客新星 第八。热爱国学的Java后端开发者&#xff0c;修心和技术同步精进。 &#x1f34e;个人主页&#xff1a;Java Fans的博客 &#x1f34a;个人信条&#xff1a;不迁怒&#xff0c;不贰过。小知识&#xff0c;大智慧。 ✨特色专栏&#xff1a…

【全网首发】2023年NOIP真题

目录 前言 真题 结尾 前言 NOIP题目了解一下&#xff0c;后续有可能会出讲解&#xff0c;题目全部来自于洛谷 真题 第一题&#xff1a;词典 第二题&#xff1a;三值逻辑 第三题&#xff1a;双序列扩展 第四题&#xff1a; 天天爱打卡 结尾 大家可以把你的预期分数打在评论…

JS判断是否存在某个元素(includes、indexOf、find、findeIndex、some)(every 数组内所有值是否相同)

方法一&#xff1a;array.includes(searcElement[,fromIndex]) 此方法判断数组中是否存在某个值&#xff0c;如果存在返回true&#xff0c;否则返回false。 searchElement&#xff1a;需要查找的元素&#xff0c;必选。fromIndex&#xff1a;可选&#xff0c;从该索引处开始查…

Android 解决CameraView叠加2个以上滤镜拍照黑屏的BUG (二)

1. 前言 这段时间&#xff0c;在使用 natario1/CameraView 来实现带滤镜的预览、拍照、录像功能。 由于CameraView封装的比较到位&#xff0c;在项目前期&#xff0c;的确为我们节省了不少时间。 但随着项目持续深入&#xff0c;对于CameraView的使用进入深水区&#xff0c;逐…

【项目管理】PMO技能树21项参照

导读&#xff1a;PMO技能树让你能够有全局视野&#xff0c;让你对照着检查自己的能力是否掌握。技能树提供了构建个人知识体系参照和地图导航&#xff0c;不至于迷失方向。 目录 1、PMO层次概览 2、技能树 2.1 项目管理流程 2.2 项目组合管理 2.3 风险管理 2.4 项目资源管…

算法:记忆化搜索

文章目录 记忆化搜索斐波那契数列 例题不同路径最长递增子序列猜数字大小矩阵中的最长递增路径 记忆化搜索的原理其实很简单&#xff0c;简单来说就是对暴力搜索的一些优化&#xff0c;因此整体上来讲难度不高 记忆化搜索 所谓记忆化搜索&#xff0c;直白来说就是一个带有备忘…

《轻购优品》新零售玩法:消费积分认购+众筹新玩法

《轻购优品》新零售玩法&#xff1a;消费积分认购众筹新玩法 引言&#xff1a;2023年开年已来&#xff0c;政府的工作报告提出“把恢复和扩大消费摆在优先位置”&#xff0c;并且把2023年定位为“消费提振年”&#xff0c;以“全年乐享全年盛惠”为主题多地政府共同发力&#x…

亚马逊云科技AI创新应用下的托管在AWS上的数据可视化工具—— Amazon QuickSight

目录 Amazon QuickSight简介 Amazon QuickSight的独特之处 Amazon QuickSight注册 Amazon QuickSight使用 Redshift和Amazon QuickSightt平台构建数据可视化应用程序 构建数据仓库 数据可视化 Amazon QuickSight简介 亚马逊QuickSight是一项可用于交付的云级商业智能 (BI…