数据结构篇其三---链表分类和双向链表

前言

数据结构篇其二实现了一个简单的单链表,链表的概念,单链表具体实现已经说明,如下:
单链表
事实上,前面的单链表本质上是无头单向不循环链表。此篇说明的双向链表可以说完全反过来了了。无论是之前的单链表还是双向链表,本质都是链表家族的两位成员。
主题一:链表分类
详细说说链表的特征,以及这些特征组合的链表种类。
主题二:双向链表的实现
像上次实现单链表一样,这次也试着独立实现双向链表吧。
学习收获:十分钟手搓一个链表

为什么学习双向链表?
因为虽然字面上双向链表好像还难一点,结构虽然复杂,但是实现起来特别简单。应用场景有显著的优势。

链表的分类

  • 单向与双向

链表的单向与双向:这说明节点与节点之间的联系。单向链表节点的指针一路往后。双向链表节点指针指前指后。
单向与双向

可见,从定义上,双向链表天生应该有两个指针,所以在单链表的基础上,我们可以推出双向链表的定义。

//双向链表的定义
typedef int LTDataType;
typedef struct ListNode {LTDataType data;//数据域struct ListNode* prev;//前驱指针struct ListNode* next;//后继指针
}ListNode;

链表双向和单向决定了它节点指针的数量

  • 带头与不带头

为了方便对链表进行操作,我们会在链表的第一个节点前附带一个头节点(哨兵位),注意头节点不是第一个节点,第一个节点存储的是有效数据。
头节点的数据域不存储有效的数据,指针域next指向第一个节点,若是双向的话,则前驱指针指向尾结点。
需注意这个时候头指针就指向头节点,而不是第一个节点了。
单链表带哨兵位

  • 循环非循环

若链表的尾结点指向头节点而不是NULL,则链表闭合形成了一个环,可以循环了,就称为循环链表。反之,则为不循环链表。
循环与非循环

  1. 以上就是链表的三大特征,每种特征又分两种情况。组合起来一共8种,所以链表种类一共8种。
  2. 下面介绍双向链表,来熟悉一下双向,带头,循环的链表吧。

双向链表的实现

下面实现这些函数

// 创建返回链表的头结点.
ListNode* ListCreate();
// 双向链表销毁
void ListDestory(ListNode* plist);
// 双向链表打印
void ListPrint(ListNode* plist);
// 双向链表尾插
void ListPushBack(ListNode* plist, LTDataType x);
// 双向链表尾删
void ListPopBack(ListNode* plist);
// 双向链表头插
void ListPushFront(ListNode * plist, LTDataType x);
// 双向链表头删
void ListPopFront(ListNode* plist);

前面已经给出了双向链表的定义。但下图只体现了双向,循环和带头还要我们具体实现。

typedef int LTDataType;
typedef struct ListNode {LTDataType data;//数据域struct ListNode* prev;//前驱指针struct ListNode* next;//后继指针
}ListNode;

开局一个头指针,能这样做吗?


int main() {ListNode* plist = NULL;return 0;
}

这个是带头的链表,起码有头节点吧,所以先创建一个头节点。
其次,这个是循环链表,头节点的前驱指针和后继指针应该都指向自己吧。
头节点

节点创建

ListNode* ListCreate(LTDataType x) {ListNode* phead = (ListNode*)malloc(sizeof(ListNode));//判断动态空间是否开辟失败。if (phead == NULL) {perror("malloc fail");exit(-1);}phead->data = x;//头节点数据随便赋值phead->next = phead;//前驱,后继指针指向自己phead->prev = phead;return phead;
}

双向链表初始化

ListNode* ListInit() {return ListCreate(-1);//头节点的数据域随便给值。
}

还没有元素啊,那就先插入节点吧

双向链表的尾插

如何实现尾插呢?

  1. 保证每个节点的两个指针有明确的指向。

  2. 尾插操作的节点有三个,头节点,尾结点,新节点。

  3. 按照上面图片的步骤写代码。

  4. 后面请自行画图分析,多创建临时变量,良好的命名习惯。写完一个函数去测试一下。

void ListPushBack(ListNode* phead, LTDataType x) {assert(phead);ListNode* newnode = ListCreate(x);//创建新节点ListNode* tail = phead->prev;//记录尾结点//执行尾插操作phead->prev = newnode;newnode->next = phead;tail->next = newnode;newnode->prev = tail;
}

打印函数

void ListPrint(ListNode* phead) {assert(phead);ListNode* pcur = phead->next;printf("head->");while (pcur!= phead) {printf("%d->", pcur->data);pcur = pcur->next;}pcur = NULL;printf("return\n");
}

双链表的头插

void ListPushFront(ListNode* phead,LTDataType x) {assert(phead);ListNode* newnode = ListCreate(x);ListNode* first = phead->next;phead->next = newnode;newnode->prev = phead;newnode->next = first;first->prev = newnode;}

双向链表的尾删

//尾删函数
void ListPopBack(ListNode* phead) {assert(phead);ListNode* tail = phead->prev;ListNode* tailprev = tail->prev;phead->prev = tailprev;tailprev->next = phead;if(phead->prev!=phead)//判断是否为空表,哨兵位不能释放了。free(tail);tail = NULL;}

双向链表的头删

/头删函数
void ListPopFront(ListNode* phead) {assert(phead);ListNode* first = phead->next;ListNode* second = first->next;phead->next = first->next;second->prev = phead;if (phead != first)//哨兵位不能释放{free(first);}first = NULL;second = NULL;
}

双向链表的销毁

void ListDestory(ListNode** pphead) {assert(pphead);assert(*pphead);ListNode* pcur = (*pphead)->next;ListNode* next = pcur->next;while (pcur != *pphead){free(pcur);pcur = next;next = next->next;}free(pcur);pcur = NULL;next = NULL;*pphead = NULL;}

双向链表补充

// 双向链表查找
ListNode* ListFind(ListNode* plist, LTDataType x);
// 双向链表在pos的前面进行插入
void ListInsert(ListNode* pos, LTDataType x);
// 双向链表删除pos位置的节点
void ListErase(ListNode* pos);

双向链表查找指定数据

ListNode* ListFind(ListNode* phead, LTDataType x) {assert(phead);ListNode* pcur = phead->next;while (pcur != phead) {if (pcur->data == x) {return pcur;//找到了返回当前节点的地址}pcur=pcur->next;}return NULL;//双链表跑完一遍都没找到,返回空。
}

在pos节点之前插入新节点

//在pos之前插入
void ListInsert(ListNode* pos, LTDataType x) {assert(pos);ListNode* newnode = ListCreate(x);ListNode* prev = pos->prev;newnode->next = pos;pos->prev = newnode;prev->next = newnode;newnode->prev = prev;pos = NULL;prev = NULL;newnode = NULL;
}

删除位置为pos的节点

void ListErase(ListNode* pos) {assert(pos);ListNode* prev = pos->prev;ListNode* next = pos->next;free(pos);prev->next = next;next->prev = prev;pos = NULL;prev = NULL;next = NULL;
}

十分钟实现一个链表

  1. 实现什么类型的链表?
  2. 需要写什么函数?

双向链表;
实现函数,增删查改,还有来链表的初始化,销毁。


#include<stdio.h>
#include<stdlib.h>
#include<assert.h>typedef int LTDataType;
typedef struct ListNode {LTDataType data;struct ListNode* next;struct ListNode* prev;
}LT;LT* LTInit(void) {LT* newnode = (LT*)malloc(sizeof(LT));if (newnode == NULL) {perror("malloc fail");exit(-1);}newnode->next = newnode;newnode->prev = newnode;return newnode;
}LT LTDestory(LT** pphead) {assert(pphead);assert(*pphead);LT* pcur = (*pphead)->next;LT* next = pcur->next;while (pcur != *pphead) {free(pcur);pcur = next;next = next->next;}free(pcur);*pphead = NULL;}//在pos之前插入新节点
void LTInsert(LT* pos, LTDataType x) {assert(pos);LT* prev = pos->prev;LT * newnode = (LT*)malloc(sizeof(LT));if (newnode == NULL) {perror("malloc fail");exit(-1);}newnode->data = x;prev->next = newnode;newnode->prev = prev;newnode->next = pos;pos->prev = newnode;}void LTErase(LT* pos) {assert(pos);LT* prev = pos->prev;LT* next = pos->next;free(pos);prev->next = next;next->prev = prev;
}void LTPushBack(LT* phead,LTDataType x) {LTInsert(phead, x);
}void LTPushFront(LT* phead, LTDataType x) {LTInsert(phead->next, x);
}void LTPopBack(LT* phead) {LTErase(phead->prev);
}void LTPopFront(LT* phead) {LTErase(phead->next);
}LT* LTFind(LT* phead, LTDataType x) {LT* pcur = phead->next;while (pcur != phead) {if (pcur->data == x) {return pcur;}pcur = pcur->next;}return NULL;return;
}LT* LTMidfy( LT* pos,LTDataType x) {pos->data = x;
}

双向链表完结。链表完结!

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

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

相关文章

Java进阶学习笔记12——final、常量

final关键字&#xff1a; final是最终的意思。可以修饰类、方法、变量。 修饰类&#xff1a;该类就被称为最终类&#xff0c;特点是不能被继承了。 修饰方法&#xff1a;该方法是最终方法&#xff0c;特点是不能被重写了。 修饰变量&#xff1a;该变量只能被赋值一次。 有些…

智慧校园的建设思路

智慧校园建设的一个主要目的就是要打破学校内的信息孤岛&#xff0c;其核心是在人、流程和信息三个层面的全面整合。智慧校园应该能够为全校师生员工及校外用户提供统一的、一站式的服务渠道&#xff1b;能够将学校各种业务流程连接起来&#xff0c;实现各种应用系统的互联互通…

postgresql insert on conflict 不存在则插入,存在则更新

向一张表执行插入动作&#xff0c;如果插入的字段数据已存在&#xff0c;则执行更新操作&#xff0c;不存在则进行插入操作。 1、创建一张表 CREATE TABLE "user_info" ( "id" int2 NOT NULL, "name" varchar(20) COLLATE "pg_catalog&quo…

基于Tensorflow卷积神经网络人脸识别公寓人员进出管理系统

欢迎大家点赞、收藏、关注、评论啦 &#xff0c;由于篇幅有限&#xff0c;只展示了部分核心代码。 文章目录 一项目简介 二、功能三、系统四. 总结 一项目简介 一、项目背景与意义 随着科技的快速发展和智能化水平的提高&#xff0c;公寓管理面临着越来越多的挑战。传统的公寓…

C++ 数据结构算法 学习笔记(32) -五大排序算法

C 数据结构算法 学习笔记(32) -五大排序算法 选择算法 如下若有多个女生的身高需要做排序: 常规思维: 第一步先找出所有候选美女中身高最高的&#xff0c;与最后一个数交换 第二步再找出除最后一位美女外其它美女中的最高者&#xff0c;与倒数第二个美女交换位置 再找出除最…

k8s-pod详解

一、Pod基本概念&#xff1a; 1.pod介绍&#xff1a; Pod是kubernetes中最小的资源管理组件&#xff0c;Pod也是最小化运行容器化应用的资源对象。一个Pod代表着集群中运行的一个进程。kubernetes中其他大多数组件都是围绕着Pod来进行支撑和扩展Pod功能的&#xff0c;例如&am…

电赛经验分享——赛前准备

⏩ 大家好哇&#xff01;我是小光&#xff0c;想要成为系统架构师的嵌入式爱好者。 ⏩在之前的电赛中取得了省一的成绩&#xff0c;本文对电赛比赛前需要准备什么做一个经验分享。 ⏩感谢你的阅读&#xff0c;不对的地方欢迎指正。 加入小光嵌入式交流群&#xff08;qq群号&…

在线人才测评在企业招聘和大学生求职中的应用场景

每年的春招秋招&#xff0c;都是毕业生们忙着找工作的季节&#xff0c;相比社招来说&#xff0c;春招秋招是每个毕业生务必重视的机会&#xff0c;大厂名企毕竟名额有限&#xff0c;如果找到自己心仪的职业岗位&#xff0c;作为毕业生就必须提前准备&#xff0c;深入了解招聘的…

五管OTA输入极性快速判断

做CMFB还有负反馈的时候曾经在判断输入输出极性上吃了大亏&#xff0c;直接做实验波形正确就是输入正端&#xff0c;全差分就不用考虑这么多了 和弯折&#xff0c;形状类似7&#xff0c;相同方向输入正端&#xff0c;相反的就是输入负端&#xff0c;输出也是和输入负端一个方向…

【NLP】人机对话

概念 机器翻译就是用计算机把一种语言翻译成另外一种语言的技术 机器翻译的产生与发展 17 世纪&#xff0c;笛卡尔与莱布尼茨试图用统一的数字代码来编写词典 1930 机器脑 1933 苏联发明家特洛阳斯基用机械方法将一种语言翻译为另一种语言 1946 ENIAC 诞生 1949 机器翻译问题…

香蕉成熟度检测YOLOV8NANO

香蕉成熟度检测YOLOV8NANO&#xff0c;采用YOLOV8NANO训练&#xff0c;得到PT模型&#xff0c;然后转换成ONNX模型&#xff0c;让OEPNCV调用&#xff0c;从而摆脱PYTORCH依赖&#xff0c;支持C。python&#xff0c;安卓开发。能检测六种香蕉类型freshripe freshunripe overripe…

Vita-CLIP: Video and text adaptive CLIP via Multimodal Prompting

标题&#xff1a;Vita-CLIP: 通过多模态提示进行视频和文本自适应CLIP 源文链接&#xff1a;https://openaccess.thecvf.com/content/CVPR2023/papers/Wasim_Vita-CLIP_Video_and_Text_Adaptive_CLIP_via_Multimodal_Prompting_CVPR_2023_paper.pdfhttps://openaccess.thecvf.…

ue5 中ps使用记录贴

一、快捷键记录 放大图形 ctrlalt空格 放大图形 缩小视口 ctrl空格 ctrlD 取消选区 ctrlt缩小文字 w魔棒工具 选择魔棒的时候把容差打开的多一点 二、案例 移动文字 在相应的图层选择 移动文字 修改图片里的颜色 在通道里拷贝红色通道&#xff0c;复制红色通道粘贴给正常图…

大模型应用商业化落地关键:给企业带来真实的业务价值

2024 年被很多人称为大模型应用的元年&#xff0c;毫无疑问&#xff0c;大模型已经成为共识&#xff0c;下一步更急迫的问题也摆在了大家的面前——大模型到底能够用在哪&#xff1f;有哪些场景能落地&#xff1f;怎么做才能创造真正的价值&#xff1f; 在刚刚过去的 AICon 全…

【排序算法】快速排序(四个版本以及两种优化)含动图)

制作不易&#xff0c;三连支持一下吧&#xff01;&#xff01;&#xff01; 文章目录 前言一.快速排序Hoare版本实现二.快速排序挖坑法版本实现三.快速排序前后指针版本实现四.快速排序的非递归版本实现五.两种优化总结 前言 前两篇博客介绍了插入和选择排序&#xff0c;这篇博…

halcon配合yolov8导出onnx模型检测物体

1.工业上多数视觉开发都是用halcon开发的&#xff0c;halcon本身也有自己的深度学习网络&#xff0c;至于halcon如果不使用本身的深度学习&#xff0c;使用其他网络导出的onnx模型怎样配合使用&#xff1f;本文基于yolov8写一个列子。 2。创建输入数据的转换代码 #region 创建输…

【bugfix】/usr/local/bin/docker-compose:行1: html: 没有那个文件或目录

前言 在使用 docker-compose 管理容器化应用时&#xff0c;偶尔会遇到一些意想不到的错误&#xff0c;比如当尝试运行 docker-compose 命令时&#xff0c;终端非但没有展示预期的输出&#xff0c;反而出现类似网页错误的信息。这类问题通常与 docker-compose 的安装或配置有关…

首都师范大学聘请旅美经济学家向凌云为客座教授

2024年4月17日&#xff0c;首都师范大学客座教授聘任仪式在首都师范大学资源环境与旅游学院举行。首都师范大学资源环境与旅游学院院长吕拉昌主持了仪式&#xff0c;并为旅美经济学家向凌云教授颁发了聘书。 吕拉昌院长指出&#xff0c;要贯彻教育部产学研一体化战略&#xff0…

虚拟机Centos扩展磁盘空间

虚拟机空间&#xff1a;现sda大小20G&#xff0c;因课程需要扩容 在虚拟机扩容中&#xff0c; 新增一块硬盘 和 直接在原有硬盘基础上扩容是一样的&#xff08;只不过在原有硬盘上扩容需要关机才可以执行&#xff09;&#xff1b; 但两者都最好先做数据备份或快照&#xff0c…

【LabVIEW FPGA入门】同步C系列模块

1.同步使用循环定时器VI计时循环速率的系列模块 数字模块SAR ADC 模块多路复用模块 数字通道可以在一个时钟周期内执行。模拟通道需要多个时钟周期。 同步模拟模块的每个通道有一个 ADC&#xff0c;采集的数据在通道之间没有明显的偏差。多路复用模块使用多路复用器通过单个 A…