【探索数据结构】线性表之单链表

🎉🎉🎉欢迎莅临我的博客空间,我是池央,一个对C++和数据结构怀有无限热忱的探索者。🙌

🌸🌸🌸这里是我分享C/C++编程、数据结构应用的乐园✨

🎈🎈🎈期待与你一同在编程的海洋中遨游,探索未知的技术奥秘💞

📝专栏指路:

📘【C++】专栏:深入解析C++的奥秘,分享编程技巧与实践。

📘【数据结构】专栏:探索数据结构的魅力,助你提升编程能力。

前言

回顾:线性表之顺序表

顺序表的问题及思考

1.中间/头部的插入删除,时间复杂度为0(N)

2.增容需要申请新空间,拷贝数据,释放旧空间。会有不小的消耗。

3.增容一般是呈2倍的增长,势必会有一定的空间浪费。例如当前容量为100,满了以后增容到200,我们再继续插入了5个数据,后面没有数据插入了,那么就浪费了95个数据空间。

思考:如何解决以上问题呢?

是否存在一种数据结构,能够解决以上顺序表表现出来的问题:

1)中间/头部的插入删除,可以一步到位,不需要挪动数据

2)不需要扩容

3)不会造成空间

答案是本篇主角——(单)链表

文章重点介绍:不带头节点不循环的单链表

一、链表

链表是一种物理存储单元上非连续、非顺序的存储结构,其数据元素的逻辑顺序是通过链表中的指针链接次序实现的。链表由一系列结点组成,每个结点都包含两个部分:数据域和指针域。数据域用于存储数据元素的信息,而指针域则存储着指向下一个结点的地址信息。有单链表、双链表、循环链表

优点:动态内存分配、插入和删除高效、空间利用率高

缺点:访问元素效率低、有额外的空间开销、缓存不优好

二、单链表

1.概念

单链表是一种线性数据结构,其中元素在逻辑上按照线性顺序排列,但在物理内存中通过指针进行链接。每个元素(称为节点/结点)都包含两部分信息:数据域和指针域。数据域存储节点的值或数据,而指针域则包含指向下一个节点的指针。

56d7df8925e9452ebb85358278a7a2a0.png

2.分类

(1)带头节点

在单链表中,第一个节点之前通常有一个头指针(或称为头节点),它指向链表中的第一个数据节点。如果链表为空,则头指针通常指向空(NULL)。链表的最后一个节点的指针域被设置为空(NULL),以表示链表的结束。

(2)不带头节点

不带头节点的单链表,我们直接通过头指针指向第一个数据节点,而不需要额外的头节点作为起始点。

3.代码示例

创建单链表结构

typedef int SLTDataType;
typedef struct SListNode SLTNode;
struct SListNode
{SLTDataType data;//节点存放的数据SLTNode* next;//存放下一个节点的地址
};

三、对单链表的操作

(0)打印

6582f2cea45549c58cf56b7f343c2311.png

//打印
void SLTPrint(SLTNode* phead)
{//创建指向第一个节点的指针的指针,让phead无需改变SLTNode* pcur = phead;while (pcur){printf("%d->", pcur->data);pcur = pcur->next;}printf("NULL\n");
}

(1)创建一个新节点

//创建一个新节点
SLTNode* SLTBuyNode(SLTDataType x)
{SLTNode* newnode = (SLTNode*)malloc(sizeof(SLTNode));if (newnode == NULL){perror("malloc fail");exit(1);//申请空间成功会返回0}newnode->data = x;newnode->next = NULL;return newnode;
}

(2)尾插

有链表为空和非空两种情况

28f492b698964650aaf1fa8ebb8146d0.png

//尾插
void SLTPushBack(SLTNode** pphead, SLTDataType x)
{assert(pphead);//*pphead是指向第一个节点的指针, 存放第一个节点的地址//创建一个新节点SLTNode* newnode = SLTBuyNode(x);//空链表if (*pphead == NULL){*pphead = newnode;}else{//非空链表,找尾//创建指向第一个节点的指针的指针,让*phead无需改变SLTNode * ptail = *pphead;while (ptail->next){ptail = ptail->next;}//新节点变为链表的新尾巴ptail->next = newnode;}
}

思考:为什么要用二级指针来接收实参?

9de62bf3fc9b4e759b9b2f7c358ff376.png

简单来说,想要改变实参,传值无法改变,要通过传实参的地址给形参,形参的改变才会影响实参

(3)头插

//头插
void SLTPushFront(SLTNode** pphead, SLTDataType x)
{assert(pphead);//*pphead是指向第一个节点的指针, 存放第一个节点的地址//创建一个新节点SLTNode* newnode = SLTBuyNode(x);newnode->next = *pphead;*pphead = newnode;
}

(4)查找

//查找
SLTNode* SLTFind(SLTNode* phead, SLTDataType x)
{assert(phead);//不改变头结点,防止要多次遍历时找不到头结点SLTNode* pcur = phead;while (pcur){if (pcur->data == x){return pcur;}pcur = pcur->next;}return NULL;
}

(5)头删

//头删
void SLTDelFront(SLTNode** pphead)
{assert(pphead && *pphead);//链表不能为空//无论有几个节点都适用SLTNode* next = (*pphead)->next;//保存第二个节点的地址free(*pphead);*pphead = NULL;*pphead = next;//原本的第二个节点成为新头结点
}

(6)尾删

be0ffab1e3644b3bade0122546b802b1.png

//尾删
void SLTDelBack(SLTNode** pphead)
{//可能会修改头节点地址所以还是使用二级指针assert(pphead && *pphead);//链表不能为空//链表只有一个节点if ((*pphead)->next == NULL)//->优先级高于*{free(*pphead);*pphead = NULL;}//多个节点SLTNode* ptail = *pphead;SLTNode* prev = *pphead;while (ptail->next){prev = ptail;ptail = ptail->next;}prev->next = NULL;free(ptail);ptail = NULL;
}

(7)指定位置之前插入

//指定位置之前插入
void SLTInsert(SLTNode** pphead, SLTNode* pos, SLTDataType x)
{assert(pphead && *pphead);//链表不能为空assert(pos);//创建一个新节点SLTNode* newnode = SLTBuyNode(x);//相当于头插if ((*pphead) == pos){//pphead存放指向第一个节点指针的地址SLTPushFront(pphead, x);}else{SLTNode* prev = *pphead;while (prev->next != pos){prev = prev->next;}//prev->newnode->posnewnode->next = pos;prev->next = newnode;}
}

(8)指定位置之后插入

//指定位置之后插入
void SLTInsertAfter(SLTNode* pos, SLTDataType x)
{//为什么不需要头结点,因为在指定位置插入是这样的逻辑//pos-newnode-pos->next,通过pos我们就能找到他的下一个节点assert(pos);//创建一个新节点SLTNode* newnode = SLTBuyNode(x);newnode->next = pos->next;pos->next = newnode;//思考:上面两行代码顺序互换可以吗?
}

6bdb67d133f349e99d19319d65fbca05.png

不相同,不可以互换位置,第二中代码pos->next指针先指向了newnode那么原先pos的下一个节点的地址就会找不到

(9)删除指定节点

//删除指定节点pos
void SLTErase(SLTNode** pphead, SLTNode* pos)
{assert(pphead && *pphead);//链表不能为空assert(pos);if ((*pphead) == pos){//相当于头删SLTDelFront(pphead);}else{SLTNode* prev = *pphead;while (prev->next!=pos){prev = prev->next;}prev->next = pos->next;free(pos);pos = NULL;}
}

(10)删除指定节点之后的数据

// 删除指定节点pos之后的数据
void SLTEraseAfter(SLTNode* pos)
{assert(pos);//如果没有用del来存放pos->next会怎么样?SLTNode* del = pos->next;pos->next = del->next;free(del);del = NULL;
}

(11)销毁

一个一个销毁

/销毁
void SLTDestroy(SLTNode** pphead)
{assert(pphead && *pphead);//链表不能为空SLTNode* pcur = *pphead;while (pcur){//如果没有保存pcur->next的地址// 在pcur释放掉后,剩余节点找不到了,无法销毁SLTNode* next = pcur->next;free(pcur);pcur = next;}*pphead = NULL;
}

四、测试(仅供参考)

#include"SList.h"
void STListTest01()
{//创建节点SLTNode* node1 = (SLTNode*)malloc(sizeof(SLTNode));node1->data = 1;SLTNode* node2 = (SLTNode*)malloc(sizeof(SLTNode));node2->data = 2;SLTNode* node3 = (SLTNode*)malloc(sizeof(SLTNode));node3->data = 3;//链接节点node1->next = node2;node2->next = node3;node3->next = NULL;//创建指向第一个节点的指针,让node1无需改变SLTNode* plist = node1;//调用打印SLTPrint(plist);}
void STListTest02()
{SLTNode* plist = NULL;SLTNode* p = NULL;//尾插SLTPushBack(&plist, 1);SLTPushBack(&plist, 2);SLTPushBack(&p, 3);plist->next->next = p;SLTPushBack(&p, 4);SLTPrint(plist);//头插SLTPushFront(&plist, 5);SLTPrint(plist);//尾删SLTDelBack(&plist);SLTPrint(plist);//头删SLTDelFront(&plist);SLTPrint(plist);SLTNode*find=SLTFind(plist, 3);//指定位置之前插入SLTInsert(&plist, find, 5);SLTPrint(plist);//指定位置之后插入SLTInsertAfter(find, 10);SLTPrint(plist);//删除指定节点/*SLTErase(plist, find);*///删除指定节点之后数据SLTEraseAfter(find);SLTPrint(plist);//销毁链表SLTDestroy(&plist);SLTPrint(plist);}
int main()
{//STListTest01();STListTest02();
}

下回预告:线性表之双链表

持续更新中...

敬请期待

 

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

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

相关文章

Autodl服务器中Faster-rcnn(jwyang)复现(一)

前言 在做实验时需要用到faster-rcnn做对比,本节首先完成代码复现,用的数据集是VOC2007~ 项目地址:https://github.com/jwyang/faster-rcnn.pytorch/tree/pytorch-1.0 复现环境:autodl服务器+python3.6+cuda11.3+Ubuntu20.04+Pytorch1.10.0 目录 一、环境配置二、编译cud…

2024年软考总结 信息系统管理师

选择题 英文题,我是一题也没把握,虽然我理解意思。 千万不要认为考死记硬背不对。目的不在于这。工程项目中有很多重要的数字,能记住说明你合格。 案例 几乎把答案全写在案例中了。 计算题 今年最简单。没有考成本。 只考了关键路径&a…

安卓开发:相机水印设置

1.更新水印 DecimalFormat DF new DecimalFormat("#"); DecimalFormat DF1 new DecimalFormat("#.#");LocationManager LM (LocationManager)getSystemService(Context.LOCATION_SERVICE); LM.requestLocationUpdates(LocationManager.GPS_PROVIDER, 2…

【学习笔记】计算机组成原理(七)

指令系统 文章目录 指令系统7.1 机器指令7.1.1 指令的一般格式7.1.2 指令字长 7.2 操作数类型和操作类型7.2.1 操作数类型7.2.2 数据在存储器中的存放方式7.2.3 操作类型 7.3 寻址方式7.3.1 指令寻址7.3.1.1 顺序寻址7.3.1.2 跳跃寻址 7.3.2 数据寻址7.3.2.1 立即寻址7.3.2.2 直…

精品PPT | 精益生产管理中MES系统的实现与应用(免费下载)

【1】关注本公众号,转发当前文章到微信朋友圈 【2】私信发送 MES系统的实现与应用 【3】获取本方案PDF下载链接,直接下载即可。 如需下载本方案PPT/WORD原格式,请加入微信扫描以下方案驿站知识星球,获取上万份PPT/WORD解决方案&…

Redis - 缓存场景

学习资料 学习的黑马程序员哔站项目黑马点评,用作记录和探究原理。 Redis缓存 缓存 :就是数据交换的缓冲区,是存储数据的临时地方,读写性能较高 缓存常见的场景: 数据库查询加速:通过将频繁查询的数据缓存起来&…

嵩山为什么称为三水之源

三水指黄河、淮河、济河,这三条河流环绕在嵩山周边。 黄河横亘在嵩山北部,其支流伊洛河从西南方环绕嵩山,然后汇入黄河。济河,古称济水,源自济源王屋山,自身河道在东晋时代被黄河夺占,从此消失。…

毕设 大数据校园卡数据分析

文章目录 0 前言1 课题介绍2 数据预处理2.1 数据清洗2.2 数据规约 3 模型建立和分析3.1 不同专业、性别的学生与消费能力的关系3.2 消费时间的特征分析 4 Web系统效果展示5 最后 0 前言 🔥 这两年开始毕业设计和毕业答辩的要求和难度不断提升,传统的毕设…

【vue2配置】Vue Router

Vue Router官网 1、npm install vue-router4 2、创建模块,在src目录小创/views/map/MapIndex.vue模块和创router/index.js文件 3、在router/index.js配置路由 import Vue from "vue"; import Router from "vue-router"; // 引入模块 const Ma…

智慧校园建设的进阶之路

智慧校园的建设现已到达了老练的阶段,许多学校设备充满着数字化信息,进出宿舍楼,校园一卡通体系会记载下学生信息,外来人员闯入会报警,翻开电脑就能查到学生是否在宿舍等……学生的学习和日子都充满了数字化的痕迹。但…

光速入门python的OpenCV

前言 欢迎来到我的博客 个人主页:北岭敲键盘的荒漠猫-CSDN博客 本文整理python的OpenCV模块的关键知识点 争取用最短的时间入门OpenCV 并且做到笔记功能直接复制使用 OpenCV简介 不浪费时间的介绍: 就是类似于ps操作图片。 至于为什么不直接用ps,因为只有程序能…

【找出满足差值条件的下标 I】python

目录 暴力题解 优化:滑动窗口维护大小值 暴力题解 class Solution:def findIndices(self, nums: List[int], indexDifference: int, valueDifference: int) -> List[int]:nlen(nums)for i in range(n):for j in range(n-1,-1,-1):if abs(i-j)>indexDiffere…

海康威视NVR通过ehome协议接入视频监控平台,视频浏览显示3011超时错误的问题解决,即:The request timeout! 【3011】

目录 一、问题描述 二、问题分析 2.1 初步分析 2.2 查看日志 2.3 问题验证 1、查看防火墙 2、查看安全组 3、问题原因 三、问题解决 3.1 防火墙开放相关端口 3.2 安全组增加规则 3.3 测试 1、TCP端口能够联通的情况 2、TCP端口不能够联通的情况 四、验证 五、云…

「51媒体」如何与媒体建立良好关系?

传媒如春雨,润物细无声,大家好,我是51媒体网胡老师。 与媒体建立良好关系对于企业或个人来说都是一项重要的公关活动。 了解媒体:研究媒体和记者的兴趣,提供相关且有价值的信息。 建立联系:通过专业的方式…

牛客NC324 下一个更大的数(三)【中等 双指针 Java/Go/PHP/C++】参考lintcode 52 · 下一个排列

题目 题目链接: https://www.nowcoder.com/practice/475da0d4e37a481bacf9a09b5a059199 思路 第一步:获取数字上每一个数,组成数组arr 第二步:利用“下一个排列” 问题解题方法来继续作答,步骤:利用lintc…

C++进阶之路:何为拷贝构造函数,深入理解浅拷贝与深拷贝(类与对象_中篇)

✨✨ 欢迎大家来访Srlua的博文(づ ̄3 ̄)づ╭❤~✨✨ 🌟🌟 欢迎各位亲爱的读者,感谢你们抽出宝贵的时间来阅读我的文章。 我是Srlua小谢,在这里我会分享我的知识和经验。&am…

PostgreSQL基础(三):PostgreSQL的基础操作

文章目录 PostgreSQL的基础操作 一、用户操作 二、权限操作 三、操作任务

解决CLion调试时无法显示变量值的问题

1 问题描述 使用CLion的时候,调试时无法显示变量的值,例如: 图来自StackOverflow。 2 解决办法 可以尝试切换调试器解决,在Linux下,CLion支持GDB和LLDB,如果GDB不行,可以切换到LLDB。 切换方…

软件杯 题目: 基于深度学习的疲劳驾驶检测 深度学习

文章目录 0 前言1 课题背景2 实现目标3 当前市面上疲劳驾驶检测的方法4 相关数据集5 基于头部姿态的驾驶疲劳检测5.1 如何确定疲劳状态5.2 算法步骤5.3 打瞌睡判断 6 基于CNN与SVM的疲劳检测方法6.1 网络结构6.2 疲劳图像分类训练6.3 训练结果 7 最后 0 前言 🔥 优…

为什么单片机不能直接驱动继电器和电磁阀

文章是瑞生网转载,PDF格式文章下载: 为什么单片机不能直接驱动继电器和电磁阀.pdf: https://url83.ctfile.com/f/45573183-1247189072-10b6d1?p7526 (访问密码: 7526)