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

🎉🎉🎉欢迎莅临我的博客空间,我是池央,一个对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…

头歌OpenGauss数据库-I.复杂查询第8关:两门及以上课程不及格的学生

任务描述 本关任务:根据提供的表和数据,查询两门及其以上不及格课程的同学的学号(s_id)、姓名(s_name)及其平均成绩(avg_score),要求计算平均成绩后为整数。 student表数据: s_ids_names_sex01Mia女02Riley男03Aria女04Lucas女05Oliver男06Caden男07Lily女08Jacob男c…

安卓开发:相机水印设置

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 直…

第四十五天 | 322.零钱兑换

题目:322.零钱兑换 尝试解答: 1.确定dp[j]含义:装满容量为j的背包所需要放的硬币个数为dp[j]; 2.动态转移方程:dp[j] dp[j - coins[i]] 1; 3.遍历顺序:本题应该为组合类题目,不考虑装入的顺序&#x…

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

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

吃掉 N 个橘子的最少天数(Lc1553)——记忆化搜索

厨房里总共有 n 个橘子,你决定每一天选择如下方式之一吃这些橘子: 吃掉一个橘子。如果剩余橘子数 n 能被 2 整除,那么你可以吃掉 n/2 个橘子。如果剩余橘子数 n 能被 3 整除,那么你可以吃掉 2*(n/3) 个橘子。 每天你只能从以上 …

Redis - 缓存场景

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

【挖金子game】

如果您想要编写一个简单的“挖金子”游戏代码,可以使用Python这样的编程语言来实现。以下是一个简单的Python代码示例,用于创建一个基本的“挖金子”游戏: import random # 游戏设置 max_gold 10 # 最大金子数量 max_digs 5 # 最大挖掘…

数据驱动(Data-Driven)和以数据为中心(Data-Centric)的区别

一、什么是数据驱动? 数据驱动(Data-Driven)是在管理科学领域经常提到的名词。数据驱动决策(Data-Driven Decision Making,简称DDD)是一种方法论,即在决策过程中主要依赖于数据分析和解释&…

Java基础学习:java中的基础注解

在Java中,有一些内置的(或称为“基础”)注解(annotation),这些注解在Java标准库中定义,并且具有特定的用途。以下是一些主要的Java内置注解: Override: 用于表示一个方法…

Keras深度学习框架第二十七讲:KerasTuner超参数优化基础

1、超参数优化概念 1.1 什么是超参数优化 超参数调优,也称为超参数优化或参数调优,是寻找学习算法或模型最佳超参数组合的过程。超参数是在训练过程开始之前设置的参数,模型无法直接从数据中学习这些参数。它们控制着学习算法的行为&#x…

NDIS小端口驱动开发(二)

初始化微型端口适配器 当网络设备可用时,系统会加载所需的 NDIS 微型端口驱动程序。 随后,即插即用 (PnP) 管理器向 NDIS 发送即插即用 IRP 来启动设备。 NDIS 调用微型端口驱动程序的 MiniportInitializeEx 函数来初始化用于网络 I/O 操作的适配器。 初…

嵩山为什么称为三水之源

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

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

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

职场不是挣钱

职场怎么不是挣钱? 曾经我也一直这么想,只要做好老板安排的事情,自然就可以挣到钱了。 目的应该是没错的,是挣钱。 只是做好活就能挣钱,好像想得有些简单了。 毕竟每个人都在干活,为什么就该自己挣钱呢&a…

【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…

C语言——在头⽂件中#if、_STDC_等字⾏起什么作⽤?

一、问题 通常,⼀些程序员都不会去研究头⽂件中的内容是什么含义,总觉得乱乱的,有很多 #if、_STDC_、#line 等字符,那么这些字符都各代表什么呢,在头⽂件中又起到什么作⽤呢? 二、解答 在头⽂件中存在类似…

智慧校园建设的进阶之路

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