数据结构:线性表之-单向链表(无头)
目录
什么是单向链表
顺序表和链表的区别和联系
顺序表:
链表:
链表表示(单项)和实现
1.1 链表的概念及结构
1.2单链表(无头)的实现
所用文件
将有以下功能:
链表定义
创建新链表元素
尾插
头插
尾删
头删
查找-给一个节点的指针
改
pos位置之前插入
删除pos位置的值
成品展示
SList.h
SList.c
test.c
什么是单向链表
单向链表是一种常见的线性数据结构,它由一系列节点组成,每个节点包含两部分:数据和指向下一个节点的指针。每个节点只能访问它后面的节点,而不能访问前面的节点。
单向链表的特点:
- 每个节点包含数据和指向下一个节点的指针。
- 最后一个节点的指针指向空值(NULL),表示链表的结束。
- 可以动态地添加或删除节点,链表的长度可以根据需要进行扩展或缩小。
- 可以根据指针迅速插入或删除节点,而不需要移动其他节点。
单向链表相对于数组来说,具有一些优点和缺点:
- 优点:插入和删除元素的时间复杂度为O(1),不需要像数组一样进行元素的移动;链表长度可以动态调整,没有固定大小的限制。
- 缺点:要访问特定位置的元素需要从头开始遍历,时间复杂度为O(n);相比于数组,在使用额外的指针存储下一个节点的信息,会占用更多的内存空间。
由于单向链表的特点,它常常被用于需要频繁插入和删除元素的场景,或者在事先无法确定数据大小和数量的情况下使用。
顺序表和链表的区别和联系
顺序表:
优点:
空间连续、支持随机访问
缺点:
- 中间或前面部分的插入删除时间复杂度O(N)
- 2.增容的代价比较大。
链表:
缺点:
以节点为单位存储,不支持随机访问
优点:
- 任意位置插入删除时间复杂度为O(1)
- 没有增容消耗,按需申请节点空间,不用了直接释放。
链表表示(单项)和实现
1.1 链表的概念及结构
概念:链表是一种物理存储结构上非连续、非顺序的存储结构,数据元素的逻辑顺序是通过链表
中的指针链接次序实现的
1.2单链表(无头)的实现
- 无头单向非循环链表:结构简单,一般不会单独用来存数据。实际中更多是作为其他数据结
构的子结构,如哈希桶、图的邻接表等等。另外这种结构在笔试面试中出现很多。- 带头双向循环链表:结构最复杂,一般用在单独存储数据。实际中使用的链表数据结构,都
是带头双向循环链表。另外这个结构虽然结构复杂,但是使用代码实现以后会发现结构会带
来很多优势,实现反而简单了,后面我们代码实现了就知道了。
所用文件
定义三个文件:
- 头文件 SList.h
- 函数的实现SList.c
- 代码的测试test.c
将有以下功能:
//打印链表
void SListPrint(SLTNode* phead);//创建新链表元素(动态申请一个节点)
SLTNode* BuySListNode(SLTDataType x);//尾插
void SListPushBack(SLTNode** pphead, SLTDataType x);//头插
void SListPushFront(SLTNode** pphead, SLTDataType x);//尾删
void SListPopBack(SLTNode** pphead);//头删
void SListPopFront(SLTNode** pphead);//查找->可在查找的基础上进行修改SListAlter
SLTNode* SListFind(SLTNode* phead,SLTDataType x);//改
void SListAlter(SLTNode* phead, SLTDataType x,SLTDataType y);//pos位置之前插入
void SListInsert(SLTNode** pphead, SLTNode* pos, SLTDataType x);//删除pos位置的值
void SListErase(SLTNode** pphead, SLTNode* pos);
链表定义
定义链表基本结构
typedef struct SListNode
{int data;struct SListNode* next;
}SLTNode;
创建新链表元素
创建新元素用于插入原链表
SLTNode* BuySListNode(SLTDataType x)
{//开辟空间SLTNode* newnode = (SLTNode*)malloc(sizeof(SLTNode));assert(newnode);newnode->data = x;newnode->next = NULL;return newnode;
}
尾插
void SListPushBack(SLTNode** pphead, SLTDataType x)
{//开辟空间SLTNode* newnode = BuySListNode(x);if (*pphead == NULL){//防止开始时节点为空*pphead = newnode;}else{//找尾节点SLTNode* tail = *pphead;//找到链表首元素while (tail->next != NULL){//检索到未节点tail = tail->next;}//插入tail->next = newnode;}
}
头插
void SListPushFront(SLTNode** pphead, SLTDataType x)
{SLTNode* newnode = BuySListNode(x);newnode->next = *pphead;*pphead = newnode;//地址传给pphead //*pphead=&plist/*头插无需检查是否为空*/
}
尾删
void SListPopBack(SLTNode** pphead)
{assert(*pphead);if ((*pphead)->next==NULL){//1,只有一个节点free(*pphead);*pphead = NULL;}else{//2,有多个节点//将前一个链元素中存放的地址换为NULL,防止野指针/* 写法一 */SLTNode* tailPrev = NULL;SLTNode* tail = *pphead;while (tail->next!=NULL){tailPrev = tail;//tail的地址tail = tail->next;}free(tail);tailPrev->next = NULL;/* //写法二* //tail寻找的是倒数第二个元素while (tail->next->next!=NULL){tail = tail->next;}free(tail->next);tail->next = NULL;*/}
}
头删
void SListPopFront(SLTNode** pphead)
{ //防止被删空assert(*pphead);//找到首位链表元素SLTNode* next = (*pphead)->next;//存储首元素存放下一个元素的地址free(*pphead);//释放首元素*pphead = next;//将第二位元素改为首元素
}
查找-给一个节点的指针
//无需更改元素,故传一级指针
SLTNode* SListFind(SLTNode* phead, SLTDataType x)
{SLTNode* cur = phead;while (cur){if (cur->data==x)return cur;cur = cur->next;}//未找到指定元素,返回NULLreturn NULL;
}
改
改元素是建立再查找基础之上进行更改
void SListAlter(SLTNode* phead, SLTDataType x, SLTDataType y)
{printf("修改成功:\n");//先找到相应元素,再进行更改SLTNode* ret = SListFind(phead, y);ret->data = x;
}
pos位置之前插入
任意位置之前插入
void SListInsert(SLTNode** pphead, SLTNode* pos, SLTDataType x)
{assert(pphead);assert(pos);//头插if (pos == *pphead)SListPushFront(pphead, x);else{ //任意位置之前插入SLTNode* prev = *pphead;while (prev->next!=pos)//找到pos的位置{prev = prev->next;//prev存放pos的地址}//找到位置SLTNode* newnode = BuySListNode(x);//创建新链表元素,并赋值prev->next = newnode;//给前一个元素赋上下一元素地址newnode->next = pos;//给插入元素存放下一个元素的地址}
}
删除pos位置的值
void SListErase(SLTNode** pphead, SLTNode* pos)
{assert(pphead);assert(pos);if (*pphead == pos)SListPopFront(pphead);//头删else{SLTNode* prev = *pphead;while (prev->next != pos)//找到pos的位置{prev = prev->next;//prev存放pos的地址//移到pos前一位,next存放的是pos的地址}//将prev存放的地址改为pos后一个元素的地址prev->next = pos->next;//释放posfree(pos);pos = NULL;}
}
成品展示
SList.h
#pragma once#include <stdlib.h>
#include <stdio.h>
#include <assert.h>typedef int SLTDataType;typedef struct SListNode
{int data;struct SListNode* next;
}SLTNode;//打印链表
void SListPrint(SLTNode* phead);//创建新链表元素(动态申请一个节点)
SLTNode* BuySListNode(SLTDataType x);//尾插
void SListPushBack(SLTNode** pphead, SLTDataType x);//头插
void SListPushFront(SLTNode** pphead, SLTDataType x);//尾删
void SListPopBack(SLTNode** pphead);//头删
void SListPopFront(SLTNode** pphead);//查找->可在查找的基础上进行修改SListAlter
SLTNode* SListFind(SLTNode* phead,SLTDataType x);void SListAlter(SLTNode* phead, SLTDataType x,SLTDataType y);//pos位置之前插入
void SListInsert(SLTNode** pphead, SLTNode* pos, SLTDataType x);//删除pos位置的值
void SListErase(SLTNode** pphead, SLTNode* pos);
SList.c
#include "SList.h"//打印
void SListPrint(SLTNode* phead)
{SLTNode* cur = phead;while (cur!=NULL){printf("%d->", cur->data);cur = cur->next;//存放下一个元素的地址}printf("NULL\n");
}//创建新链表元素
SLTNode* BuySListNode(SLTDataType x)
{SLTNode* newnode = (SLTNode*)malloc(sizeof(SLTNode));assert(newnode);newnode->data = x;newnode->next = NULL;return newnode;
}//尾插
void SListPushBack(SLTNode** pphead, SLTDataType x)
{assert(pphead);SLTNode* newnode = BuySListNode(x);//dataif (*pphead == NULL){//防止开始时节点为空*pphead = newnode;}else{//找尾节点SLTNode* tail = *pphead;while (tail->next != NULL){//存放新节点地址tail = tail->next;}tail->next = newnode;}
}//头插
void SListPushFront(SLTNode** pphead, SLTDataType x)
{assert(pphead);SLTNode* newnode = BuySListNode(x);newnode->next = *pphead;*pphead = newnode;//地址传给pphead //*pphead=&plist/*头插无需检查是否为空*/
}//尾删
void SListPopBack(SLTNode** pphead)
{assert(*pphead);if ((*pphead)->next==NULL){//1,只有一个节点free(*pphead);*pphead = NULL;}else{//2,有多个节点//将前一个链元素中存放的地址换为NULL,防止野指针/* 写法一 */SLTNode* tailPrev = NULL;SLTNode* tail = *pphead;while (tail->next!=NULL){tailPrev = tail;//tail的地址tail = tail->next;}free(tail);tailPrev->next = NULL;/* //写法二* //tail寻找的是倒数第二个元素while (tail->next->next!=NULL){tail = tail->next;}free(tail->next);tail->next = NULL;*/}
}//头删
void SListPopFront(SLTNode** pphead)
{ //防止被删空assert(*pphead);SLTNode* next = (*pphead)->next;free(*pphead);*pphead = next;
}//查找-给一个节点的指针
SLTNode* SListFind(SLTNode* phead, SLTDataType x)
{SLTNode* cur = phead;while (cur){if (cur->data==x)return cur;cur = cur->next;}return NULL;
}//改
void SListAlter(SLTNode* phead, SLTDataType x, SLTDataType y)
{assert(phead);printf("修改成功:\n");SLTNode* ret = SListFind(phead, y);ret->data = x;
}
//pos位置之前插入
void SListInsert(SLTNode** pphead, SLTNode* pos, SLTDataType x)
{assert(pphead);assert(pos);//头插if (pos == *pphead)SListPushFront(pphead, x);else{ SLTNode* prev = *pphead;while (prev->next!=pos)//找到pos的位置{prev = prev->next;//prev存放pos的地址}//找到位置SLTNode* newnode = BuySListNode(x);//创建新链表元素,并赋值prev->next = newnode;//给前一个元素赋上下一元素地址newnode->next = pos;//给插入元素存放下一个元素的地址}
}//删除pos位置的值
void SListErase(SLTNode** pphead, SLTNode* pos)
{assert(pphead);assert(pos);if (*pphead == pos)SListPopFront(pphead);//头删else{SLTNode* prev = *pphead;while (prev->next != pos)//找到pos的位置{prev = prev->next;//prev存放pos的地址//移到pos前一位,next存放的是pos的地址}//将prev存放的地址改为pos后一个元素的地址prev->next = pos->next;//释放posfree(pos);pos = NULL;}
}
test.c
test.c仅仅是用于测试代码,本文以弄懂单向链表为主,故不进行菜单制作
但改测试中也包含了对部分链表结构即原理进行了讲解,请耐心看完
#include "SList.h"
void TestSList1()
{ SLTNode* n1 = (SLTNode*)malloc(sizeof(SLTNode));assert(n1);SLTNode* n2 = (SLTNode*)malloc(sizeof(SLTNode));assert(n2);SLTNode* n3 = (SLTNode*)malloc(sizeof(SLTNode));assert(n3);SLTNode* n4 = (SLTNode*)malloc(sizeof(SLTNode));assert(n4);n1->data=1;n2->data=2;n3->data=3;n4->data=4;n1->next = n2;n2->next = n3;n3->next = n4;n4->next = NULL;SListPrint(n1);
}void TestSList2()
{SLTNode* plist = NULL;//传的是plist指针的地址//如果直接传plist,将导致SLTNode* phead中//phead为临时拷贝,不影响实参SListPushBack(&plist, 1);SListPushBack(&plist, 2);SListPushBack(&plist, 3);SListPushBack(&plist, 4);SListPrint(plist);//不改变无需传二级指针SListPushFront(&plist,0);SListPrint(plist);SListPopFront(&plist);SListPrint(plist);SListPopBack(&plist);/*SListPrint(plist);SListPopBack(&plist);SListPrint(plist);SListPopBack(&plist);SListPrint(plist);SListPopBack(&plist);SListPrint(plist);*//*SListPopBack(&plist);SListPrint(plist);*/
}void TestSList3()
{SLTNode* plist = NULL;//传的是plist指针的地址//如果直接传plist,将导致SLTNode* phead中//phead为临时拷贝,不影响实参SListPushBack(&plist, 1);SListPushBack(&plist, 2);SListPushBack(&plist, 3);SListPushBack(&plist, 4);SListPrint(plist);//不改变无需传二级指针//查找SLTNode* ret = SListFind(plist, 3);if (ret){//返回值不为空则为找到printf("找到了\n");}SListPrint(plist);修改//SListAlter(plist, 20, 2);//SListPrint(plist);//插入SLTNode* pos = SListFind(plist, 3);//先要找到再进行更改if (pos){SListInsert(&plist, pos, 40);}SListPrint(plist);//删除pos = SListFind(plist, 2);//先要找到再进行删除if (pos){SListErase(&plist, pos);}SListPrint(plist);
}int main()
{TestSList3();return 0;
}
单向链表讲解完毕啦!创作不易,如果喜欢请留下个赞吧,感激不尽😊
相关文章:

数据结构:线性表之-单向链表(无头)
目录 什么是单向链表 顺序表和链表的区别和联系 顺序表: 链表: 链表表示(单项)和实现 1.1 链表的概念及结构 1.2单链表(无头)的实现 所用文件 将有以下功能: 链表定义 创建新链表元素 尾插 头插 尾删 头删 查找-给一个节点的…...

为IT服务台构建自定义Zia操作
Zia是manageengine的商业人工智能助手,是ServiceDesk Plus Cloud的虚拟会话支持代理。使用Zia,您可以优化帮助台管理,还可以缩小最终用户与其帮助台之间的差距,Zia通过执行预配置的操作来帮助用户完成他们的服务台任务。 例如&…...
【C/C++】BMP格式32位转24位
问题 如题 解决方法 bmp文件格式参考:【C/C++】BITMAP格式分析_vc++ bitmap头文件_sunriver2000的博客-CSDN博客BITMAP文件大体上分成四个部分,如下表所示。文件部分长度(字节)位图文件头 Bitmap File Header14位图信息数据头 Bitmap Info Header40调色板 Palette4*n (n≥…...

合宙Air724UG LuatOS-Air LVGL API控件-滑动条 (Slider)
滑动条 (Slider) 滑动条看起来和进度条是有些是有些像,但不同的是滑动条可以进行数值选择。 示例代码 -- 回调函数 slider_event_cb function(obj, event)if event lvgl.EVENT_VALUE_CHANGED then local val (lvgl.slider_get_value(obj) or "0")..&…...

SQLAlchemy 封装的工具类,数据库pgsql(数据库连接池)
1.SQLAlchemy是什么? SQLAlchemy 是 Python 著名的 ORM 工具包。通过 ORM,开发者可以用面向对象的方式来操作数据库,不再需要编写 SQL 语句。 SQLAlchemy 支持多种数据库,除 sqlite 外,其它数据库需要安装第三方驱动。…...

【Git】Git 基础
Git 基础 参考 Git 中文文档 — https://git-scm.com/book/zh/v2 1.介绍 Git 是目前世界上最先进的分布式版本控制系统,有这么几个特点: 分布式:是用来保存工程源代码历史状态的命令行工具保存点:保存点可以追溯源码中的文件…...

腾讯云AI绘画:探究AI创意与技术的新边界
目录 一、2023的“网红词汇”——AI绘画二、智能文生图1、智能文生图的应用场景2、风格和配置的多样性3、输入一段话,腾讯云AI绘画给你生成一张图4、文本描述生成图像,惊艳全场 三、智能图生图:重新定义图像美学1、智能图生图的多元应用场景2…...
离线数仓同步数据1
用户行为表数据同步 2.1.4 日志消费Flume测试 [gpbhadoop104 ~]$ cd /opt/module/flume/ [gpbhadoop104 flume]$ cd job/ [gpbhadoop104 job]$ rm file_to_kafka.confcom.atguigu.gmall.flume.interceptor.TimestampInterceptor$Builder #定义组件 a1.sourcesr1 a1.channelsc1…...

c语言开篇---跟着视频学C语言
标识符 标识符必须声明定义,可以是变量、函数或其他实体。 Int是标识符吗? 不是,int是c语言关键词,不是随意命名的 C语言关键词如下: 常量 不需要被声明,不能赋值更改。 printf函数 printf是由print打印…...
本地yum源-如学
学不学? 如学~ 到底学不学? 如学~ 学? 如学~ 配置本地的镜像yum 使用到的 rpm 包 是根据centos8 里面自带的 在 /dev/cdrom 中包含着 一些系统自带的 rpm # 先将 /dev/cdrom 设备进行挂载 mkdir /up # 在…...

【实训】“宅急送”订餐管理系统(程序设计综合能力实训)
👀樊梓慕:个人主页 🎥个人专栏:《C语言》《数据结构》《蓝桥杯试题》《LeetCode刷题笔记》《实训项目》 🌝每一个不曾起舞的日子,都是对生命的辜负 前言 大一小学期,我迎来了人生中的第一次实训…...

openeuler上安装polarismesh集群
1、安装MySQL数据库 数据库连接地址10.10.10.168 用户root 密码123456 MySQL安装参考搭建DSS环境(六)之安装基础环境MySQL_linux安装dss_青春不流名的博客-CSDN博客 2、安装Redis集群 IPResid PORTSentinel PORTPASSWORDCluster NAME10.10.10.110637…...
Java基础——stream
流 stream是什么?stream优点stream和集合的区别stream的创建steam的操作从steam中取值 stream是什么? stream可以简化对集合的操作,具体操作由流内部实现,而无需用户自行实现过程 stream优点 对于以下ArrayList List<Strin…...
Spring Quartz 持久化解决方案
Quartz是实现了序列化接口的,包括接口,所以可以使用标准方式序列化到数据库。 而Spring2.5.6在集成Quartz时却未能考虑持久化问题。 Spring对JobDetail进行了封装,却未实现序列化接口,所以持久化的时候会产生NotSerializable问题&…...

基于Java+SpringBoot+Vue前后端分离火锅店管理系统设计和实现
博主介绍:✌全网粉丝30W,csdn特邀作者、博客专家、CSDN新星计划导师、Java领域优质创作者,博客之星、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java技术领域和毕业项目实战✌ 🍅文末获取源码联系🍅 👇🏻 精彩专…...

Unity——导航系统补充说明
一、导航系统补充说明 1、导航与动画 我们可以通过设置动画状态机的变量,让动画匹配由玩家直接控制的角色的移动。那么自动导航的角色如何与动画系统结合呢? 有两个常用的属性可以获得导航代理当前的状态: 一是agent.velocity,…...
nginx实现负载均衡load balance
目录 nginx实现负载均衡load balance相关算法负载均衡https的访问后端的real server是否知道真正访问的用户的IP地址健康检查提升负载均衡的并发数量七层负载均衡和四层负载均衡七层负载均衡四层负载均衡四层和七层的区别502错误 nginx实现负载均衡load balance 准备ÿ…...
淘宝订单接口:连接消费者与商家的桥梁
当我们谈论淘宝订单接口时,我们谈论的是淘宝网为卖家和买家提供的一个用于处理订单的核心系统。通过这个接口,卖家可以接收订单、处理订单状态,并更新买家和平台的状态信息;买家则可以实时追踪自己的订单状态,更好地掌…...

数据结构-第一期——数组(Python)
目录 00、前言: 01、一维数组 一维数组的定义和初始化 一维变长数组 一维正向遍历 一维反向遍历 一维数组的区间操作 竞赛小技巧:不用从a[0]开始,从a[1]开始 蓝桥杯真题练习1 读入一维数组 例题一 例题二 例题三 实战训…...

八 动手学深度学习v2 ——卷积神经网络之卷积+填充步幅+池化+LeNet
目录 1. 图像卷积总结2. 填充和步幅 padding和stride3. 多输入多输出通道4. 池化层5. LeNet 1. 图像卷积总结 二维卷积层的核心计算是二维互相关运算。最简单的形式是,对二维输入数据和卷积核执行互相关操作,然后添加一个偏置。核矩阵和偏移是可学习的参…...

超短脉冲激光自聚焦效应
前言与目录 强激光引起自聚焦效应机理 超短脉冲激光在脆性材料内部加工时引起的自聚焦效应,这是一种非线性光学现象,主要涉及光学克尔效应和材料的非线性光学特性。 自聚焦效应可以产生局部的强光场,对材料产生非线性响应,可能…...

04-初识css
一、css样式引入 1.1.内部样式 <div style"width: 100px;"></div>1.2.外部样式 1.2.1.外部样式1 <style>.aa {width: 100px;} </style> <div class"aa"></div>1.2.2.外部样式2 <!-- rel内表面引入的是style样…...

SiFli 52把Imagie图片,Font字体资源放在指定位置,编译成指定img.bin和font.bin的问题
分区配置 (ptab.json) img 属性介绍: img 属性指定分区存放的 image 名称,指定的 image 名称必须是当前工程生成的 binary 。 如果 binary 有多个文件,则以 proj_name:binary_name 格式指定文件名, proj_name 为工程 名&…...

浪潮交换机配置track检测实现高速公路收费网络主备切换NQA
浪潮交换机track配置 项目背景高速网络拓扑网络情况分析通信线路收费网络路由 收费汇聚交换机相应配置收费汇聚track配置 项目背景 在实施省内一条高速公路时遇到的需求,本次涉及的主要是收费汇聚交换机的配置,浪潮网络设备在高速项目很少,通…...
libfmt: 现代C++的格式化工具库介绍与酷炫功能
libfmt: 现代C的格式化工具库介绍与酷炫功能 libfmt 是一个开源的C格式化库,提供了高效、安全的文本格式化功能,是C20中引入的std::format的基础实现。它比传统的printf和iostream更安全、更灵活、性能更好。 基本介绍 主要特点 类型安全:…...

论文阅读:LLM4Drive: A Survey of Large Language Models for Autonomous Driving
地址:LLM4Drive: A Survey of Large Language Models for Autonomous Driving 摘要翻译 自动驾驶技术作为推动交通和城市出行变革的催化剂,正从基于规则的系统向数据驱动策略转变。传统的模块化系统受限于级联模块间的累积误差和缺乏灵活性的预设规则。…...

rknn toolkit2搭建和推理
安装Miniconda Miniconda - Anaconda Miniconda 选择一个 新的 版本 ,不用和RKNN的python版本保持一致 使用 ./xxx.sh进行安装 下面配置一下载源 # 清华大学源(最常用) conda config --add channels https://mirrors.tuna.tsinghua.edu.cn…...
Spring Boot + MyBatis 集成支付宝支付流程
Spring Boot MyBatis 集成支付宝支付流程 核心流程 商户系统生成订单调用支付宝创建预支付订单用户跳转支付宝完成支付支付宝异步通知支付结果商户处理支付结果更新订单状态支付宝同步跳转回商户页面 代码实现示例(电脑网站支付) 1. 添加依赖 <!…...

AxureRP-Pro-Beta-Setup_114413.exe (6.0.0.2887)
Name:3ddown Serial:FiCGEezgdGoYILo8U/2MFyCWj0jZoJc/sziRRj2/ENvtEq7w1RH97k5MWctqVHA 注册用户名:Axure 序列号:8t3Yk/zu4cX601/seX6wBZgYRVj/lkC2PICCdO4sFKCCLx8mcCnccoylVb40lP...
Windows 下端口占用排查与释放全攻略
Windows 下端口占用排查与释放全攻略 在开发和运维过程中,经常会遇到端口被占用的问题(如 8080、3306 等常用端口)。本文将详细介绍如何通过命令行和图形化界面快速定位并释放被占用的端口,帮助你高效解决此类问题。 一、准…...