链接未来:深入理解链表数据结构(一.c语言实现无头单向非循环链表)
在上一篇文章中,我们探索了顺序表这一基础的数据结构,它提供了一种有序存储数据的方法,使得数据的访 问和操作变得更加高效。想要进一步了解,大家可以移步于上一篇文章:探索顺序表:数据结构中的秩序之美
今天,我们将进一步深入,探讨另一个重要的数据结构——链表
链表和顺序表一样,都属于线性表,也用于存储数据,但其内部结构和操作方式有着明显的不同。通过C语言的具体实现,我们将会更加直观地理解它
源码可以打我的gitee里面查找:唔姆/比特学习过程2 (gitee.com)
文章目录
- @[toc]
- 一.链表的概念及结构
- 二.链表的分类
- 三.无头单向非循环链表的实现
- 1.项目文件规划
- 2.基本结构及功能一览
- 3.各功能接口具体实现
- 3.1打印单链表
- 3.2尾插
- 3.3头插
- 3.4尾删
- 3.5头删
- 3.6查找
- 3.7插入pos前一个
- 3.8删除pos前一个
- 3.9插入pos后一个
- 3.10删除pos后一个
- 3.11销毁(避免内存泄露)
文章目录
- @[toc]
- 一.链表的概念及结构
- 二.链表的分类
- 三.无头单向非循环链表的实现
- 1.项目文件规划
- 2.基本结构及功能一览
- 3.各功能接口具体实现
- 3.1打印单链表
- 3.2尾插
- 3.3头插
- 3.4尾删
- 3.5头删
- 3.6查找
- 3.7插入pos前一个
- 3.8删除pos前一个
- 3.9插入pos后一个
- 3.10删除pos后一个
- 3.11销毁(避免内存泄露)
一.链表的概念及结构
链表是一种物理存储(实际上)结构上==非连续、非顺序==(杂乱随意排序)的存储结构,数据元素的逻辑顺序是通过链表中的指针链接次序实现的
实际情况中:
从上图可发现:
- 链表在逻辑上连续,在物理上是不连续的
- 各个节点(Node)一般都是从堆上面申请空间的
- 从堆上面申请的空间是有一定策略的,可能连续,可也能不连续
二.链表的分类
- 单向或者双向
- 带头或者不带头
- 循环或者非循环
三种情况随意组合起来就有8种链表结构
其中,最为常用的是:
无头单向非循环和带头双向循环
无头单向非循环链表:结构简单,但是一般不会单独用来存数据。实际中更多是作为其他数据结构的子结构,如哈希桶、图的邻接表等等
带头双向循环链表:结构最复杂,一般用在单独存储数。实际中使用的链表数据结构,都是带头双向循环链表。这个结构虽然结构复杂,但是使用代码实现以后会发现结构会带来很多优势,实现它反而简单了
这两种结果都会给大家实现的,今天先来无头单向非循环链表
三.无头单向非循环链表的实现
1.项目文件规划
- 头文件SList.h:用来基础准备(常量定义,typedef),链表表的基本框架,函数的声明
- 源文件SList.h:用来各种功能函数的具体实现
- 源文件test.c:用来测试功能是否有问题,进行基本功能的使用
2.基本结构及功能一览
#pragma once
#include<stdio.h>
#include<assert.h>
#include<stdlib.h>typedef int SLDataType;typedef struct SingleListNode
{int data;SingleListNode* next;
}SLNode;void SLPrint(SLNode* phead);// 单链表打印void SLPushBack(SLNode** pphead, int n);// 单链表尾插
void SLPushFront(SLNode** pphead, int n);// 单链表头插
void SLPopBack(SLNode** pphead);// 单链表尾删
void SLPopFront(SLNode** pphead);// 单链表尾删SLNode* SLFind(SLNode* phead, int n);
SLNode* SLInsert(SLNode** pphead, SLNode* pos, int n);//在pos前面插入
SLNode* SLErase(SLNode** pphead, SLNode* pos);//删除pos前面那个void SLInsertAfter(SLNode* pos, int n);//在pos后面插入
void SLEraseAfter(SLNode* pos);//在pos后面删除void SLDestory(SLNode** pphead);
3.各功能接口具体实现
3.1打印单链表
void SLPrint(SLNode* phead)
{assert(phead);SLNode* cur = phead;while (cur != NULL)//与while(cur)同样的效果{printf("%d ", cur->data);cur = cur->next;}printf("\n");
}
3.2尾插
SLNode* CreateNode(int n)
{SLNode* newNode= (SLNode*)malloc(sizeof(SLNode));if (newNode == NULL){perror("malloc error");return -1;}newNode->data = n;newNode->next = NULL;return newNode;
}void SLPushBack(SLNode** pphead, int n)
{assert(pphead);SLNode* newNode = CreateNode(n);//先把节点搞好//先考虑一下没有节点的情况if (*pphead == NULL){*pphead = newNode; //这就是传二级指针的原因://我们要改变 SLNode* phead本身的指向,就把他地址传过来//当我们只是要改变指向的结构体里的内容时只要传SLNode* phead就行了}else{SLNode* tail = *pphead;while (tail->next != NULL)//找到最后一个节点{tail = tail->next;}tail->next = newNode;}
}
- 通过
CreateNode
函数创建了一个含有数值n
的新节点newNode
- 然后根据链表是否为空进行不同的操作:
- 如果链表为空(即头指针指向空),则将新节点
newNode
赋值给头指针*pphead
- 如果链表不为空,则需要找到链表末尾的节点,通过遍历找到最后一个节点(tail),并将其
next
指针指向新节点newNode
,以将新节点插入到链表的末尾
为什么传入二级指针:
这种设计方式的原因在于需要修改指针本身的值,而不是只修改指针所指向的内容
考虑到单链表在插入节点时,可能会涉及链表头指针的修改,如果直接传递单级指针(指向头指针),在函数内部对头指针进行修改是不会反映到函数外部的==(形参是实参的临时拷贝)==。但如果使用二级指针,可以在函数内部修改指针的指向,这样修改后的指向会在函数外部保持
3.3头插
void SLPushFront(SLNode** pphead, int n)
{assert(pphead);SLNode* newNode = CreateNode(n);//先把节点搞好if (*pphead == NULL){*pphead = newNode;}else{newNode->next = (*pphead)->next;(*pphead)->next = newNode;}//或者//newNode->next = (*pphead);//*pphead = newNode;
}
- 通过
CreateNode
函数创建了一个含有数值n
的新节点newNode
- 接着,根据链表是否为空进行不同的操作:
- 如果链表为空(即头指针指向空),则将新节点
newNode
赋值给头指针*pphead
- 如果链表不为空,则将新节点
newNode
的next
指针指向当前头节点的下一个节点(原链表的第二个节点),然后将当前头节点的next
指针指向新节点newNode
,以完成插入
注释部分显示了另一种写法,通过先设置新节点的 next
指针指向当前头节点,然后再将链表的头指针指向新节点,实现了同样的插入操作
3.4尾删
void SLPopBack(SLNode** pphead)
{assert(pphead);assert(*pphead);//防止一个都没有还删if ((*pphead)->next == NULL)//只有一个{free(*pphead);*pphead = NULL;}else{//找到倒数第二个SLNode* pre_tail = *pphead;while (pre_tail->next->next != NULL){pre_tail = pre_tail->next;}free(pre_tail->next);pre_tail->next = NULL;}
}
- 检查链表头指针
*pphead
是否存在(不为 NULL),以及链表是否为空(只有一个节点)-
- 如果链表中只有一个节点,则直接释放该节点,并将链表头指针设置为 NULL,表示链表为空
- 如果链表中有多个节点,则会找到倒数第二个节点,即指向最后一个节点的前一个节点。它通过遍历链表直到找到倒数第二个节点
pre_tail
,然后释放最后一个节点,并将倒数第二个节点的next
指针设置为 NULL,表示该节点成为新的末尾节点
3.5头删
void SLPopFront(SLNode** pphead)
{assert(pphead);assert(*pphead);//防止一个都没有还删SLNode* first = (*pphead)->next;//一个和多个都适用free(*pphead);*pphead = first;
}
- 创建了一个临时指针
first
来指向原链表的第二个节点(如果存在)。这是因为要删除的是链表的头节点,为了不断开链表,需要先保存第二个节点的地址- 通过
free(*pphead)
释放掉原来的头节点,然后将链表的头指针*pphead
更新为原头节点的下一个节点first
3.6查找
SLNode* SLFind(SLNode* phead, int n)
{assert(phead);SLNode* cur = phead;while (cur != NULL)//与while(cur)同样的效果{if (cur->data == n){return cur;}cur = cur->next;}return NULL;
}
3.7插入pos前一个
void SLInsert(SLNode** pphead, SLNode* pos, int n)//在pos前面插入
{assert(pphead);assert(pos);SLNode* cur = *pphead;if (*pphead == pos)//在第一个节点前面插入{// 头插SLTPushFront(pphead, n);}else{while (cur->next != pos){cur = cur->next;}SLNode* newNode = CreateNode(n);newNode->next = cur->next;cur->next = newNode;}
}
- 如果要插入的位置
pos
就是链表的头节点*pphead
,即在第一个节点前面插入,则调用SLTPushFront
函数,直接在链表头部插入新节点newNode
- 如果要插入的位置不是头节点,则通过循环遍历链表,直到找到
pos
的前一个节点cur
,然后创建新节点newNode
并将其插入到pos
前面,完成节点的插入操作
3.8删除pos前一个
void SLErase(SLNode** pphead, SLNode* pos)
{assert(pphead);assert(pos);assert(*pphead != pos);//防止前面没有SLNode* cur = *pphead;SLNode* pre_cur = *pphead;while (cur->next != pos){pre_cur = cur;cur = cur->next;}pre_cur->next = pos;free(cur);cur = NULL;
}
3.9插入pos后一个
void SLInsertAfter(SLNode* pos, int n)
{assert(pos);SLNode* newNode =CreateNode(n);newNode->next = pos->next;pos->next = newNode;
}
- 创建一个新节点
newNode
,并将新节点的next
指针指向pos
节点原本的下一个节点,以保证链表的连续性- 将
pos
节点的next
指针指向新节点newNode
,完成了在指定节点之后插入新节点的操作
3.10删除pos后一个
void SLEraseAfter(SLNode* pos)
{assert(pos);SLNode* next = pos->next->next;free(pos->next);pos->next = NULL;pos->next = next;
}
3.11销毁(避免内存泄露)
void SLDestory(SLNode** pphead)
{assert(pphead);SLNode* cur = *pphead;SLNode* next = *pphead;while (cur!=NULL){next = cur->next;free(cur);cur = next;}*pphead = NULL;
}
循环删除每一个
Node
,最后把原本的结构体指针指向NULL
好啦,这次知识就先到这里啦!下一次大概率是双向带头循环的代码实现了。
相关文章:
链接未来:深入理解链表数据结构(一.c语言实现无头单向非循环链表)
在上一篇文章中,我们探索了顺序表这一基础的数据结构,它提供了一种有序存储数据的方法,使得数据的访 问和操作变得更加高效。想要进一步了解,大家可以移步于上一篇文章:探索顺序表:数据结构中的秩序之美 今…...
Python tkinter控件全集之组合选择框 ttk.ComboBox
Tkinter标准库 Tkinter是Python的标准GUI库,也是最常用的Python GUI库之一,提供了丰富的组件和功能,包括窗口、按钮、标签、文本框、列表框、滚动条、画布、菜单等,方便开发者进行图形界面的开发。Tkinter库基于Tk for Unix/Wind…...
Axure之中继器的使用(交互动作reperter属性Item属性)
目录 一.中继器的基本使用 二.中继器的动作(增删改查) 2.1 新增 2.2 删除 2.3 更新行 2.4 效果展示 2.5 模糊查询 三.reperter属性 在Axure中,中继器(Repeater)是一种功能强大的组件,用于创建重复…...
数字化医疗新篇章:构建智能医保支付购药系统
在迎接数字化医疗时代的挑战和机遇中,智能医保支付购药系统的建设显得尤为重要。本文将深入介绍如何通过先进的技术实现,构建一套智能、高效的医保支付购药系统,为全面建设健康中国贡献力量。 1. 引言 随着医疗科技的飞速发展,…...
11_12-Golang中的运算符
**Golang **中的运算符 主讲教师:(大地) 合作网站:www.itying.com** **(IT 营) 我的专栏:https://www.itying.com/category-79-b0.html 1、Golang 内置的运算符 算术运算符关系运算符逻辑运…...
k8s-ingress特性 9
TLS加密 创建证书 测试访问 auth认证 创建认证文件 rewrite重定向 进入域名时,会自动重定向到hostname.html 示例: 测试 版本的升级迭代,之前利用控制器进行滚动更新,在升级过程中无法做到快速回滚 更加平滑的升级࿱…...
【redis】redis系统实现发布订阅的标准模板
目录 简介参数配置代码模板 简介 Redis发布订阅功能是Redis的一种消息传递模式,允许多个客户端之间通过消息通道进行实时的消息传递。在发布订阅模式下,消息的发送者被称为发布者(publisher),而接收消息的客户端被称为…...
Python 时间日期处理库函数
标准库 datetime >>> import datetime >>> date datetime.date(2023, 12, 20) >>> print(date) 2023-12-20 >>> date datetime.datetime(2023, 12, 20) >>> print(date) 2023-12-20 00:00:00 >>> print(date.strfti…...
第二十二章 : Spring Boot 集成定时任务(一)
第二十二章 : Spring Boot 集成定时任务(一) 前言 本章知识点: 介绍使用Spring Boot内置的Scheduled注解来实现定时任务-单线程和多线程;以及介绍Quartz定时任务调度框架:简单定时调度器(Simp…...
关于“Python”的核心知识点整理大全32
目录 12.6.4 调整飞船的速度 settings.py ship.py alien_invasion.py 12.6.5 限制飞船的活动范围 ship.py 12.6.6 重构 check_events() game_functions.py 12.7 简单回顾 12.7.1 alien_invasion.py 12.7.2 settings.py 12.7.3 game_functions.py 12.7.4 ship.py …...
【krita】实时绘画 入门到精通 海报+电商+装修+人物
安装插件 首先打开comfyUI,再打开krita,出现问题提示, 打开 cd custom_nodes 输入命令 安装控件 git clone https://github.com/Acly/comfyui-tooling-nodes.git krita基础设置 设置模型 设置lora (可设置lora强度 增加更多…...
云原生系列2-CICD持续集成部署-GitLab和Jenkins
1、CICD持续集成部署 传统软件开发流程: 1、项目经理分配模块开发任务给开发人员(项目经理-开发) 2、每个模块单独开发完毕(开发),单元测试(测试) 3、开发完毕后,集成部…...
50ms时延工业相机
华睿工业相机A3504CG000 参数配置: 相机端到端理论时延:80ms 厂家同步信息,此款设备帧率上线23fps,单帧时延:43.48ms,按照一图缓存加上传输显示的话,厂家预估时延在:80ms 厂家还有…...
CPU缓存一致性问题
什么是可见性问题? Further Reading :什么是可见性问题? 缓存一致性 内存一致性 内存可见性 顺序一致性区别 CPU缓存一致性问题 由于CPU缓存的出现,很好地解决了处理器与内存速度之间的矛盾,极大地提高了CPU的吞吐能…...
35道HTML高频题整理(附答案背诵版)
1、简述 HTML5 新特性 ? HTML5 是 HTML 的最新版本,它引入了很多新的特性和元素,以提供更丰富的网页内容和更好的用户体验。以下是一些主要的新特性: 语义元素:HTML5 引入了新的语义元素,像 <article&g…...
【powershell】Windows环境powershell 运维之历史文件压缩清理
🦄 个人主页——🎐开着拖拉机回家_Linux,大数据运维-CSDN博客 🎐✨🍁 🪁🍁🪁🍁🪁🍁🪁🍁 🪁🍁🪁&am…...
【Linux】Linux线程概念和线程控制
文章目录 一、Linux线程概念1.什么是线程2.线程的优缺点3.线程异常4.线程用途5.Linux进程VS线程 二、线程控制1.线程创建2.线程终止3.线程等待4.线程分离 一、Linux线程概念 1.什么是线程 线程是进程内的一个执行流。 我们知道,一个进程会有对应的PCB,…...
Flink cdc3.0同步实例(动态变更表结构、分库分表同步)
文章目录 前言准备flink环境docker构建mysql、doris环境数据准备 通过 FlinkCDC cli 提交任务整库同步同步变更路由变更路由表结构不一致无法同步 结尾 前言 最近Flink CDC 3.0发布, 不仅提供基础的数据同步能力。schema 变更自动同步、整库同步、分库分表等增强功…...
国产Apple Find My认证芯片哪里找,伦茨科技ST17H6x芯片可以帮到您
深圳市伦茨科技有限公司(以下简称“伦茨科技”)发布ST17H6x Soc平台。成为继Nordic之后全球第二家取得Apple Find My「查找」认证的芯片厂家,该平台提供可通过Apple Find My认证的Apple查找(Find My)功能集成解决方案。…...
肺癌相关知识
写在前面 大概想了解下肺癌相关的知识,开此贴做记录,看看后续有没有相关的生信文章思路。 综述 文章名期刊影响因子Lung cancer immunotherapy: progress, pitfalls, and promisesMol Cancer37.3 常见治疗手段有surgery, radiation therapy, chemoth…...
ChimeraX使用教程-安装及基本操作
ChimeraX使用教程-安装及基本操作 1、访问https://www.cgl.ucsf.edu/chimerax/download.html进行下载,然后安装 安装完成后,显示界面 2、基本操作 1、点击file,导入 .PDB 文件。 (注:在 alphafold在线预测蛋白》点…...
【小黑嵌入式系统第十一课】μC/OS-III程序设计基础(一)——任务设计、任务管理(创建基本状态内部任务)、任务调度、系统函数
上一课: 【小黑嵌入式系统第十课】μC/OS-III概况——实时操作系统的特点、基本概念(内核&任务&中断)、与硬件的关系&实现 文章目录 一、任务设计1.1 任务概述1.2 任务的类型1.2.1 单次执行类任务(运行至完成型&#…...
Redis一些常用的技术
文章目录 第1关:Redis 事务与锁机制第2关:流水线第3关:发布订阅第4关:超时命令第5关:使用Lua语言 第1关:Redis 事务与锁机制 编程要求 根据提示,在右侧编辑器Begin-End补充代码,根据…...
基于QPainter 绘图图片绕绘制设备中心旋转
项目地址:https://gitcode.com/m0_45463480/QPainter/tree/main 获取途径:进入CSDN->GitCode直接下载或者通过git拉取仓库内容。 QPainter是Qt框架中的一个类,用于在QWidget或QPixmap等设备上进行绘图操作。它提供了丰富的绘图功能,可以用于绘制线条、图形、文本等。Q…...
计算机网络(4):网络层
网络层提供的两种服务 虚电路服务(Virtual Circuit Service)和数据报服务(Datagram Service)是在网络层(第三层)提供的两种不同的通信服务。它们主要区别在于建立连接的方式和数据传输的方式。 虚电路服务…...
动态内存分配(malloc和free、calloc和realloc)
目录 一、为什么要有动态内存分配 二、C/C中程序内存区域划分 三、malloc和free 2.1、malloc 2.2、free 四、calloc和realloc 3.1、calloc 3.2、realloc 3.3realloc在调整内存空间的是存在两种情况: 3.4realloc有malloc的功能 五、常见的动…...
C语言---井字棋(三子棋)
Tic-Tac-Toe 1 游戏介绍和随机数1.1 游戏介绍1.2 随机数的生成1.3 棋盘大小和符号 2 设计游戏2.1 初始化棋盘2.2 打印棋盘2.3 玩家下棋2.4 电脑下棋2.5 判断输赢2.6 game()函数2.7 main()函数 3 完整三子棋代码3.1 Tic_Tac_Toe.h3.2 Tic_Tac_Toe.c3.3 Test.c 4 游戏代码的缺陷 …...
[Kubernetes]3. k8s集群Service详解
在上一节讲解了k8s 的pod,deployment,以及借助pod,deployment来部署项目,但会存在问题: 每次只能访问一个 pod,没有负载均衡自动转发到不同 pod访问还需要端口转发Pod重创后IP变了,名字也变了针对上面的问题,可以借助Service来解决,下面就来看看Service怎么使用 一.Service详…...
C++ 指定范围内递增初始化一个vector<int> | Python: list(range(31, 90))
通过lambda表达式 std::iota()实现: template <typename Tp> inline void print_vec(const std::vector<Tp>& vec) {fmt::print("[{}]\n", fmt::join(vec, ", ")); }// 相当于Python的lst list(range(31, 90))const std::ve…...
【Java之数据结构与算法】
选择排序 package Code01;public class Code01_SelectionSort {public static void selectionSort(int[] arr) {if(arrnull||arr.length<2) {return;}for(int i0;i<arr.length;i) {int minIndex i;for(int ji1;j<arr.length;j) {minIndex arr[minIndex] > arr[j…...
真正免费的网站建站平台b站/推广普通话作文
Swing概述 GUI——图形用户界面。该技术是目前最为常见的应用程序模型,如手机APP、Web网站等。 Java GUI最早使用的工具包是AWT(抽象窗口工具包),这个工具包提供了一套与本地图形界面交互的接口。AWT中的图形函数与操作系统所提…...
wordpress是哪家公司的建站程序/成都网站制作费用
一、背景知识Oralce中的一张表数据量达到亿数量级后或是单表达到2G大小,查询效率似乎会明显下降。需要通过分区的方式,从行的维度对表进行划分,避免单表数据量过大分区方法有下面几类:范围,最常见,按照某列…...
石家庄网站建设吧/凡科建站客服电话
Math EXP10 格式:number : EXP10(x)说明:将x的以10为底的指数值赋给number http://www.yfdmt.com/multimedia/authorware/yufeng/function1.htm isnan isinf 在linux下有两个函数 isnan(x)isinf(x) 对应在windows下的函数: _isnan(x)!_finit…...
网站怎么申请支付宝/360营销平台
今天考完软考的网络工程师了,考的挺好的。心里没多大的感想,毕竟一直在学习,刚好最近一直在做ipsec 的实验,然后最后一题就考了。看到试题,心里的感想挺多的。人与人之间是不能架起的。事情走到今天,也已经…...
教做幼儿菜谱菜的网站/成都搜索优化排名公司
1 三次握手 TCP是面向连接的,无论哪一方向另一方发送数据之前,都必须先在双方之间建立一条连接。在TCP/IP协议中,TCP 协议提供可靠的连接服务,连接是通过三次握手🤝进行初始化的。三次握手🤝的目的是同步连…...
html门户网站开发源代码/百度软件下载
Web 不论你在 web 上做什么, 都离不开请求和响应, web请求作为某个用户交互的结果由web浏览器发送到web服务器, 在web服务器上, 会生成web响应(或应答)并发回到 web 浏览器. 如果web请求的是静态内容, 比如一个Html文件, 图像或者是存储在web服务器硬盘上的其他内容, web服务器…...