单链表的头插,尾插,头删,尾删等操作
前言
顺序表要求是具有连续的物理空间,并且数据的话是在这些空间当中是连续的存储。但这样会带来很多问题,比如说在头部或者说中间插入的话,效率不是很高;并且申请空间可能需要扩容,并且越往后一般来说都是异地扩容,虽然看起来的话是简单的调用了一下realloc,但是从底层来看的话,这个代价很大。而且扩容的话也会存在一定程度的浪费,因此就需要链表的存在。
链表的话是一个数据存一个内存块。这些内存块之间并不一定要求是物理上连续的,这些内存块之间用指针进行相关的链接,链表实际上具有八种结构。
链表其实就是用指针这么链接起来的一些个内存块。
链表当中各个节点在物理上不一定是连续的,这个节点之间也并没有任何的顺序关系(都是各自随意malloc出来的),根本就不需要去考虑顺序,孤岛之间只需要有一根桥梁即可
单链表就关注这三个有机组成部分:
节点 SLTNode
节点地址(指向节点的指针) SLTNode*
一级指针(指向链表第一个节点) phead
二级指针(指向上面的一级指针) pphead
逻辑结构与物理结构
虽然有时候平时画图的时候,画链表的时候可能会带一些箭头之类的,但是真正在内存里面是不可能有箭头这些东西的。内存里面是不可能存着箭头→这些东西的。我们把这些箭头叫做逻辑结构,是我们想象出来的,因为这些箭头比指针更为直观。
在内存里面实实在在怎么存的被称为物理结构。
我们画图的时候看起来好像指针啊什么呀都是在动的,但实际上在内存里面的话,一切都是死的,没有动,无非就是不断的给指针变量进行赋值。然后在我们脑海中直观的形象表现就是:该指针变量不断的指来指去。
逻辑结构就是我们为了方便理解,形象化画出来的;但是在计算机内存里面是十分枯燥的,实实在在数据在内存中的变化是物理结构。

单链表节点(结构体类型)的创建
从语言的角度来讲,凡是有多个数据,都需要存到结构体里面去。链表的每一个节点就是通过结构体来表示。结构体里面有当前的数值data,并且还需要一个指针。因为上一个节点需要存下一个节点的地址,这样我才有寻找的依据,这样的话,各个内存块之间才可以像链条一样这么串起来。
并且我们已经知道每一个节点实际上就是一个结构体。既然上一个节点需要存下一个节点的地址。那么因此显而易见,无非就是一个结构体指针罢了,我们叫做next。
之前在c语言当中我们也讲过,结构体里面是不能够嵌套结构体的,就是无穷套娃了。但我们刚才结构体里面并没有结构体,是一个结构体指针,大小是确定的,就是四个字节。
//节点结构体的创建
typedef int SLTDataType;
typedef struct SLTNode
{SLTDataType data;struct SLTNode* next;
}SLTNode;单链表的打印
1. 空的顺序表可以打印,当然一样道理,空的链表也可以打印,给我显示为空不就可以了
2. phead就是链表第一个节点的地址,如果这个链表是空链表,那么phead就是NULL
3. 当我为空链表的时候,phead就为NULL,在打印链表的时候不能够进行断言,不然的话空链表我就打印不出来了,直接把我程序终止了。
4. 链表的话在物理上并不是连续的,它的每一个节点都是malloc出来的。
5. 每一个节点(说白了就是结构体)都会存放下一个节点的地址,因此就需要这一很关键的一步:cur = cur -> next
6. 0就是假,非0就是真,NULL本质就是0。
//单链表的打印
void SLTPrint(SLTNode* phead)
{SLTNode* cur = phead;while (cur != NULL){printf("%d->",cur->data);cur = cur->next;}printf("NULL\n");
}单链表malloc一个新节点并赋值
//单链表malloc一个新节点并赋值
SLTNode* BuySLTNode(SLTDataType x)
{SLTNode* newnode = (SLTNode*)malloc(sizeof(SLTNode));if (newnode == NULL){perror("BuySLTNode::Malloc");return NULL;}newnode->data = x;;newnode->next = NULL;return newnode;
}单链表的尾插
1. 由于之前一样的,在这个地方是不能够进行空指针断言的,因为如果是断言的话,我在空链表的情形下想要去尾插,但是断言了的话,给我程序提前终止了,这还玩个屁啊。
2. 尾插的第一步是先得搞一个节点出来,这个地方就不像顺序表一样了,要考虑什么空间够不够啊,要不要扩容啊之类的就不需要考虑了。在这边的话就自己去malloc新创建一个节点。
3. 然后对于这个新节点newnode的值给进去,next指针为NULL。
4. 然后就是找到原先的尾巴,原尾节点的next成员要存储新尾节点的地址。
5. 刚才讲的情况都是链表不为空的情况,如果链表是空的话,这种情况需要额外处理。这时候只需要把phead指向新节点newnode就可以了。
//单链表的尾插
void SLTPushBack(SLTNode** pphead, SLTDataType x)
{SLTNode* newnode = BuySLTNode(x);if (*pphead == NULL){*pphead = newnode;}else{SLTNode* tail = *pphead;while (tail->next != NULL){tail = tail->next;}tail->next = newnode;}
}单链表的头插
1. 这个也需要分两种情况,以总是链表为空的情况,第二种就是链表非空。但是发现仅有的三行代码可以完全解决空的情况。
2. 然后发现在这个函数内部也需要创建一个新的节点。那么就是说可以把创建新的节点这个过程给他单独的提取出来。
//单链表的头插
void SLTPushFront(SLTNode** pphead, SLTDataType x)
{SLTNode* newnode = BuySLTNode(x);newnode->next = *pphead;*pphead = newnode;
}单链表的尾删
1. 删除的时候也是需要用二级指针。因为万一你要把指针置空的时候,如果你传一级指针的话,就完蛋了。那么原先那个指针就变成野指针了。
2. 链表在尾删的时候要注意分几种特殊情况,第一种情况是当前只有一个节点,第二种特殊情况就是整个链表都是空的。对于第一种情况直接free,然后把指针置空就完事儿了。对于第二种情况,直接暴力assert检查就可以。
//单链表的尾删
void SLTPopBack(SLTNode** pphead)
{assert(*pphead);if ((*pphead)->next == NULL){free(*pphead);*pphead = NULL;}else{SLTNode* prev = NULL;SLTNode* tail = *pphead;while (tail->next != NULL){prev = tail;tail = tail->next;}prev->next = NULL;free(tail);tail = NULL;}
}单链表的头删
1. 首先也可以暴力检查一下这个链表是不是空的。但头删不需要特判链表只有一个节点的情况。
//单链表的头删
void SLTPopFront(SLTNode** pphead)
{assert(*pphead);SLTNode* first = *pphead;*pphead = (*pphead)->next;free(first);first = NULL;
}单链表的初始化
这个非常简单,直接初始化就可以,因为只有一个值。直接创建一个结构体指针赋为NULL就可以
SLTNode* plist = NULL;关于单链表函数操作中的有关于assert断言的问题
不是在参数里面碰到一个指针就assert一下,不是这么一根筋的。像这边单链表的话,当phead为NULL时我们都知道此时此刻表示一个空链表,但是空链表我就不能头插,尾插插入了吗?我照样可以这么插入;空链表难道我就不能打印了吗?我照样可以打印,只是什么都没有而已。但是对于删除而言,如果此时此刻你已经是一个空链表了,那么你还删个毛线啊?所以此时此刻呢又需要用assert断言一下,这种东西都是具体问题具体分析。
关于参数为二级指针的问题
1. 尤其要注意形参的改变不会影响实参,这个其实底层要理解的话,就是得通过函数栈帧。当传址调用的时候,实际上地址也是在拷贝,但由于你是在函数里面对指针进行解引用操作,因此产生的影响是持久性的,即使调用的函数退出回来了。
2. malloc向堆区动态申请内存的时候,是随机在内存里面划分一块区域的。
3. 当你的实参是一个指针的时候,虽然这时候函数传参看起来好像也是传址调用,但实际上调用函数内部执行的各种操作,对于函数外头的这个参数指针不会发生任何影响。因为实际上无论如何都会有拷贝这个环节会发生。
4. 在这边想要着重说明的是:我们在进行函数传参的时候,这时候就必须传二级指针。我们在函数体外已经先有一个指针,我们的函数内部操作都离不开要改变该指针的指向位置,要实现这个操作,就不能传一级指针,因为这样只有我这个一级指针自己在捣鼓;如果我传指针的地址(也就是二级指针),这样子我才能在函数体内去改变函数体外这个指针所指向的位置。
5. 我这个phead是一个实实在在的结构体指针,各种有关于单链表的操作,其中都需要phead指向的位置发生变化,如果在函数里面传一级指针的话,函数里面的各种乱七八糟的运转,跟我函数外面的phead指针一点关系都没有,因为你在函数内部自己有一个参数指针(形参),也许这个形参与phead指向的地址是一样的,但是当你传一级指针的时候,在函数里面各种捣鼓运算改变的都是你形参指向的位置。如果想要改变我这个phead指针指向的位置,就必须把phead的地址当成参数传给函数,因此这个参数也就是指针的地址,显而易见即为二级指针。
相关文章:
单链表的头插,尾插,头删,尾删等操作
前言顺序表要求是具有连续的物理空间,并且数据的话是在这些空间当中是连续的存储。但这样会带来很多问题,比如说在头部或者说中间插入的话,效率不是很高;并且申请空间可能需要扩容,并且越往后一般来说都是异地扩容&…...
Qt扫盲-QProcess理论总结
QProcess理论使用总结一、概述二、使用三、通过 Channel 通道通信四、同步进程API五、注意事项1. 平台特性2. 不能实时读取一、概述 QProcess 其实更多的是与外面进程进行交互的一个工具类,通过这个类来启动外部进程,获取这个进程的标准输出,…...
JAVA进阶 —— Steam流
目录 一、 引言 二、 Stream流概述 三、Stream流的使用步骤 1. 获取Stream流 1.1 单列集合 1.2 双列集合 1.3 数组 1.4 零散数据 2. Stream流的中间方法 3. Stream流的终结方法 四、 练习 1. 数据过滤 2. 数据操作 - 按年龄筛选 3. 数据操作 - 演员信息要求…...
Ubuntu Protobuf 安装(测试有效)
安装流程 下载软件 下载自己要安装的版本:https://github.com/protocolbuffers/protobuf 下载源码编译: 系统环境:Ubuntu16(其它版本亦可),Protobuf-3.6.1 编译源码 cd protobuf# 当使用 git clone 下来的…...
驱动程序开发:FTP服务器和OpenSSH的移植与搭建、以及一些笔记
目录一、FTP服务器移植与搭建1、在ubuntu下安装vsftpd2、在window下安装FileZilla3、移植vsftpd到开发板上4、Filezilla 连接测试5、注意点二、开发板 OpenSSH 移植与使用1、移植 zlib 库2、移植 openssl 库3、移植 openssh 库4、openssh 使用测试三、关于u-boot上的操作及根文…...
优化改进YOLOv5算法之添加GIoU、DIoU、CIoU、EIoU、Wise-IoU模块(超详细)
目录 1、IoU 1.1 什么是IOU 1.2 IOU代码 2、GIOU 2.1 为什么提出GIOU 2.2 GIoU代码 3 DIoU 3.1 为什么提出DIOU 3.2 DIOU代码 4 CIOU 4.1 为什么提出CIOU 4.2 CIOU代码 5 EIOU 5.1 为什么提出EIOU 5.2 EIOU代码 6 Wise-IoU 7 YOLOv5中添加GIoU、DIoU、CIoU、…...
windows电脑pc如何使用svn获取文档和代码
一、安装svn 下载链接 也可通过其他方式下载 二、使用 2.1 随便找一个文件夹 2.2 点击右键,选择SVN Checkout 2.3输入网址 如当你在网页上访问时地址为https://10.197.78.78/!/#aaa/view/head/bbb 在这里不能直接填入,而是 https://10.197.78.78/sv…...
ROS1学习笔记:tf坐标系广播与监听的编程实现(ubuntu20.04)
参考B站古月居ROS入门21讲:tf坐标系广播与监听的编程实现 基于VMware Ubuntu 20.04 Noetic版本的环境 文章目录一、创建功能包二、创建代码2.1 以C为例2.1.1 配置代码编译规则2.1.2 编译整个工作空间2.1.2 配置环境变量2.1.4 执行代码2.2 以Python为例2.2.1 配置代码…...
力扣解法汇总1590. 使数组和能被 P 整除
目录链接: 力扣编程题-解法汇总_分享记录-CSDN博客 GitHub同步刷题项目: https://github.com/September26/java-algorithms 原题链接:力扣 描述: 给你一个正整数数组 nums,请你移除 最短 子数组(可以为 …...
Spring源码阅读(基础)
第一章:bean的元数据 1.bean的注入方式: 1.1 xml文件 1.2 注解 Component(自己写的类才能在上面加这些注解) 1.3配置类: Configuration 注入第三方数据源之类 1.4 import注解 (引用了Myselector类下…...
服务搭建篇(九) 使用GitLab+Jenkins搭建CI\CD执行环境 (上) 基础环境搭建
1.前言 每当我们程序员开发在本地完成开发之后 , 都要部署到正式环境去使用 , 在一些传统的运维体系中 , 开发与运维都是割裂的 , 开发人员不允许操作正式服务器 , 服务器只能通过运维团队来操作 , 这样可以极大的提高服务器的安全性 , 不经过安全保护的开放服务器 , 对于黑客…...
CDC 长沙站丨云原生技术研讨会:数字兴链,云化未来!
一、活动信息:活动主题:CDC 长沙站丨云原生技术研讨会活动时间:2023 年 3 月 14 日下午 14:30-17:30活动地点:长沙市岳麓区-拓维信息总部 1 楼多功能厅活动参与方式:免门票参与,戳此…...
A.特定领域知识图谱知识推理方案:知识图谱推理算法综述[二](DTransE/PairRE:基于表示学习的知识图谱链接预测算法)
推荐参考文章: A.特定领域知识图谱知识推理方案:知识图谱推理算法综述[一](基于距离的翻译模型:TransE、TransH、TransR、TransH、TransA、RotatE) A.特定领域知识图谱知识推理方案:知识图谱推理算法综述[二](DTransE/PairRE:基于表示学习的知识图谱链接预测算法) A.…...
香港酒店模拟分析项目报告--使用tableau、python、matlab
转载请标记本文出处 软件:tableau、pycharm、关系型数据库:MySQL 数据大量分析考虑电脑性能的情况。 文章目录前言一、爬虫是什么?二、使用tableau数据可视化1.引入数据1.1 制作直方图-各地区酒店数量条形图1.2 各地区酒店均价1.3 价格等级堆…...
第18天-商城业务(商品检索服务,基于Elastic Search完成商品检索)
1.构建商品检索页面 1.1.引入依赖 <!-- thymeleaf模板引擎 --><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-thymeleaf</artifactId></dependency><!-- 热更新 --><…...
5.2 对射式红外传感器旋转编码器计次
对射式红外传感器1.1 接线图VCC GND分别接电源的正负极DO数字输出端,随意选择一个GPIO口1.2 硬件原理当挡光片或者编码盘在对射式红外传感器中间经过时,DO就会输出电平变化信号,电平跳变信号触发STM32 PB14号口中断,在中断函数中执…...
【数据库概论】第九章 关系查询处理和查询优化
第九章 关系查询处理和查询优化 本章主要介绍关系数据库查询管理和查询优化,主要分为代数优化(又称逻辑优化)和物理优化(也称非代数优化)。 9.1 关系型数据库系统的查询处理 查询处理是关系型数据库管理系统执行查询…...
(WIP) my cloud test bed (by quqi99)
作者:张华 发表于:2023-03-10 版权声明:可以任意转载,转载时请务必以超链接形式标明文章原始出处和作者信息及本版权声明 问题 想创建一个local local test bed, 用来方便做各种云实验,如openstack, k8s, ovn, lxd等…...
git | git 2023 详细版
文章目录一、Git命令1.2 设计用户签名1.3 初始化本地库1.4 查看本地库状态1.5 添加至暂存区1.6 从暂存区删除1.7 将暂存区的文件提交到本地库1.8 查看版本信息二、Git分支2.1 查看分支2.2 创建分支2.3 切换分支2.4 合并分支三、GitHub3.1 代码克隆clone3.2 给库取别名3.3 推送本…...
camunda流程引擎基本使用(笔记)
文章目录一、camunda基础1.1 安装与部署流程引擎1.2 流程引擎结构1.3 流程引擎的基本使用1.3.1 创建一个BPMN Diagram1.3.2 实现一个外部工作者1.3.3 部署流程1.3.4 创建一个流程实例并消费1.3.5 向流程中添加用户任务1.3.6 添加网关1.3.7 业务规则二、Java 集成流程引擎2.1 为…...
深入浅出:JavaScript 中的 `window.crypto.getRandomValues()` 方法
深入浅出:JavaScript 中的 window.crypto.getRandomValues() 方法 在现代 Web 开发中,随机数的生成看似简单,却隐藏着许多玄机。无论是生成密码、加密密钥,还是创建安全令牌,随机数的质量直接关系到系统的安全性。Jav…...
【JVM】- 内存结构
引言 JVM:Java Virtual Machine 定义:Java虚拟机,Java二进制字节码的运行环境好处: 一次编写,到处运行自动内存管理,垃圾回收的功能数组下标越界检查(会抛异常,不会覆盖到其他代码…...
江苏艾立泰跨国资源接力:废料变黄金的绿色供应链革命
在华东塑料包装行业面临限塑令深度调整的背景下,江苏艾立泰以一场跨国资源接力的创新实践,重新定义了绿色供应链的边界。 跨国回收网络:废料变黄金的全球棋局 艾立泰在欧洲、东南亚建立再生塑料回收点,将海外废弃包装箱通过标准…...
ArcGIS Pro制作水平横向图例+多级标注
今天介绍下载ArcGIS Pro中如何设置水平横向图例。 之前我们介绍了ArcGIS的横向图例制作:ArcGIS横向、多列图例、顺序重排、符号居中、批量更改图例符号等等(ArcGIS出图图例8大技巧),那这次我们看看ArcGIS Pro如何更加快捷的操作。…...
Rapidio门铃消息FIFO溢出机制
关于RapidIO门铃消息FIFO的溢出机制及其与中断抖动的关系,以下是深入解析: 门铃FIFO溢出的本质 在RapidIO系统中,门铃消息FIFO是硬件控制器内部的缓冲区,用于临时存储接收到的门铃消息(Doorbell Message)。…...
Springboot社区养老保险系统小程序
一、前言 随着我国经济迅速发展,人们对手机的需求越来越大,各种手机软件也都在被广泛应用,但是对于手机进行数据信息管理,对于手机的各种软件也是备受用户的喜爱,社区养老保险系统小程序被用户普遍使用,为方…...
安宝特方案丨船舶智造的“AR+AI+作业标准化管理解决方案”(装配)
船舶制造装配管理现状:装配工作依赖人工经验,装配工人凭借长期实践积累的操作技巧完成零部件组装。企业通常制定了装配作业指导书,但在实际执行中,工人对指导书的理解和遵循程度参差不齐。 船舶装配过程中的挑战与需求 挑战 (1…...
JS设计模式(4):观察者模式
JS设计模式(4):观察者模式 一、引入 在开发中,我们经常会遇到这样的场景:一个对象的状态变化需要自动通知其他对象,比如: 电商平台中,商品库存变化时需要通知所有订阅该商品的用户;新闻网站中࿰…...
【MATLAB代码】基于最大相关熵准则(MCC)的三维鲁棒卡尔曼滤波算法(MCC-KF),附源代码|订阅专栏后可直接查看
文章所述的代码实现了基于最大相关熵准则(MCC)的三维鲁棒卡尔曼滤波算法(MCC-KF),针对传感器观测数据中存在的脉冲型异常噪声问题,通过非线性加权机制提升滤波器的抗干扰能力。代码通过对比传统KF与MCC-KF在含异常值场景下的表现,验证了后者在状态估计鲁棒性方面的显著优…...
HubSpot推出与ChatGPT的深度集成引发兴奋与担忧
上周三,HubSpot宣布已构建与ChatGPT的深度集成,这一消息在HubSpot用户和营销技术观察者中引发了极大的兴奋,但同时也存在一些关于数据安全的担忧。 许多网络声音声称,这对SaaS应用程序和人工智能而言是一场范式转变。 但向任何技…...
