数据结构——链表(单链表)

大家好,又是我(小锋),今天给大家带了一个比较有挑战的章节(链表),但是不用担心,小锋会陪大家一起度过。

顺序表的思考与问题

1. 中间/头部的插入删除,时间复杂度为O(N)
我们在进行一些插入删除操作时我们会先内存覆盖然后再进行操作,覆盖内存的时间复杂度为O(N)。
2. 增容需要申请新空间,拷贝数据,释放旧空间。会有不小的消耗。
当我们进行扩容时我们是不是要用realloc函数,而realloc函数再扩容时分为异地扩容,和原地扩容,当我们异地扩容时又有一系列的操作这又加大了消耗。
3. 增容一般是呈2倍的增长,势必会有一定的空间浪费。例如当前容量为100,满了以后增容到
200,我们再继续插入了5个数据,后面没有数据插入了,那么就浪费了95个数据空间。
我们增加容量后可能用不完,这就会造成空间的浪费,以及顺序表的空间是连续的我们要释放就必须全部释放。
那我们今天讲到链表就不会出现上面的问题,但也不是说链表就没有缺点,不论是顺序表还是链表都有优缺点,重点在于我们怎么运用。

链表

概念:链表是一种物理存储结构上非连续、非顺序的存储结构,数据元素的逻辑顺序是通过链表
中的指针链接次序实现的 。
我们这节主要讲解单链表
这个就是链表的逻辑结构了,一个节点中有指向下一个节点的指针,这样走下去最后一个节点中放的是空指针NULL,像链条一样一环扣一环。
我给大家画一个物理结构方便大家理解
我们可以形象看出链表的基本结构
那我们来创建一个链表
我们先来实现一个打印链表内容的函数

单链表打印

//链表输出
 void LBexport(LBbiao* att) {
	 assert(att);
	 LBbiao* ps = att;
	 while (ps!= NULL) {
		 printf("%d->", ps->SZ);
		 ps = ps->next;
	 }
	 printf("NULL\n");
 }

这里大家可以画图一步一步的理一下理解会更加深刻主要注意循环的判断条件。

我们下面来实现一个插入函数

单链表头插数据

我们想一想在链表的头部插入数据我们只需要开辟一个链表空间mon然后另它的next指向当前链表的表头的地址,并将mon的地址交给表头的指针是不是就可以插入数据并且将链表连接起来了?

大家可以看看大致原理如图
我们还要注意要进行断言防止出现错误
//链表头插数据
 void LBcutin(LBbiao** att,CMMlet n) {
	 assert(att);
	 LBbiao* pt = LBopen();
	 if (*att == NULL) {
		 *att = pt;
	 }
	 else {
		 pt->next = *att;
		 *att = pt;
	 }
	 pt->SZ = n;
 }
这里开辟一个新的链表空间我们还会用到所有我们用一个函数来实现
//开辟链表
 LBbiao* LBopen() {
	 LBbiao* mon = (LBbiao*)malloc(sizeof(CMMlet) + sizeof(LBbiao*));
	 if (mon == NULL) {
		 printf("开辟失败:%s",strerror(errno));
		 return NULL;
	 }
	 mon->next = NULL;
	 mon->SZ = 0;
	 return mon;
 }
我们这里可以用老方法,分装一个函数来验证

单链表头删数据

还是我们用图来演示

//链表头删数据
 void LBdelete(LBbiao**att) {
	 assert(*att != NULL);
	 assert(att != NULL);//断言
	 LBbiao* ps = *att;
	 *att = ps->next;
	 ps->next = NULL;
	 free(ps);
 }

我们验证试试

单链表尾删数据

我们还是画图演示原理

我们可以看见尾删数据直接将倒数第二个节点的next为空就行了,当然我们还要断言一些情况

并且我们还要分情况当链表只有一个节点时该怎么删。

//链表尾删数据
 void LBweishan(LBbiao*att) {
	 assert(att != NULL);//断言
	 LBbiao* ps = att;
	 while (ps->next->next != NULL) {
		 ps = ps->next;
	 }
	 free(ps->next);
	 ps->next = NULL;
 }

单链表尾插数据

老规矩画图理解

我们通过图可以看出尾插数据要开辟一个新的节点然后让原链表的最后一个节点的next指向新开辟出来的节点并将新开辟的节点的next为空。同样我们要分情况讨论

//链表尾插数据
 void LBweicha(LBbiao** att,CMMlet n) {
	 LBbiao* ps = *att;
	 LBbiao* pt = LBopen();
	 if (ps == NULL) {
		 *att = pt;
	 }
	 else {
		 while (ps->next != NULL) {
			 ps = ps->next;
		 }
		 ps->next = pt;
	 }
	 pt->SZ = n;
 }

单链表查找

我们可以直接遍历链表如果遇到我们就返回该数据的节点地址,如果没找到我们返回NULL。

//链表查找
 LBbiao* LBchazhao(LBbiao* att, CMMlet n) {
	 assert(att);
	 LBbiao* ps = att;
	 while (ps != NULL) {
		 if (ps->SZ == n) {
			 return ps;
			 break;
		 }
		 ps = ps->next;
	 }
	 return NULL;
 }

我们可以测试一下

单链表在pos位置之后插入x

与头插类似我们主要注意的是要不要分情况讨论,以及断言的内容,具体插入的过程我们大家可以自己画图试试

接下来我们来实现一下这个函数

//链表在pos位置后插入x

 void LBporcr(LBbiao* pos, CMMlet x) {
	 assert(pos);
	 LBbiao* ps = pos;
	 LBbiao* pt = LBopen();
	 if (pos->next == NULL) {//相当于尾插数据
		 LBweicha(&pos, x);
	 }
	 else {
		//一般情况
		 pt->next=ps->next;
		 ps->next = pt;
		 pt->SZ = x;
	 }

 }

验证试试

单链表删除pos位置之后的值

我们还是用图说话

分析后我们发现我们只需要将pos位置的next指向pos->next->next就ok了,最后还要注意一下特殊情况以及断言

//链表在pos位置后删除x
 void LBpossc(LBbiao* pos) {
	 assert(pos&&pos->next!=NULL);
	 if (pos->next->next == NULL) {//相当于尾删
		 free(pos->next);
		 pos->next = NULL;
	 }
	 else {
		 LBbiao* ps = pos->next;
		 pos->next = pos->next->next;
		 ps->next = NULL;
		 free(ps->next);

	 }
 }

我们可以测试一下

我们的单链表就讲解完了是不是畅快淋漓呢?

这是我们本节的所有代码大家可以自己试试

# define _CRT_SECURE_NO_WARNINGS 1
# include<stdio.h>
# include<assert.h>
# include<string.h>
# include<errno.h>
# include<stdlib.h>



//类型重命名
//方便后期修改
typedef struct LBbiao LBbiao;
typedef int CMMlet;
//链表创建
 struct LBbiao{
	CMMlet SZ;//链表存储的数据
	struct LBbiao* next;//指向下一个节点

 };


 //开辟链表
 LBbiao* LBopen() {
	 LBbiao* mon = (LBbiao*)malloc(sizeof(CMMlet) + sizeof(LBbiao*));
	 if (mon == NULL) {
		 printf("开辟失败:%s",strerror(errno));
		 return NULL;
	 }
	 mon->next = NULL;
	 mon->SZ = 0;
	 return mon;
 }

 //链表输出
 void LBexport(LBbiao* att) {
	 assert(att);
	 LBbiao* ps = att;
	 while (ps!= NULL) {
		 printf("%d->", ps->SZ);
		 ps = ps->next;
	 }
	 printf("NULL\n");
 }

 //链表头插数据
 void LBcutin(LBbiao** att,CMMlet n) {
	 assert(att);
	 LBbiao* pt = LBopen();
	 if (*att == NULL) {
		 *att = pt;
	 }
	 else {
		 pt->next = *att;
		 *att = pt;
	 }
	 pt->SZ = n;
 }

 //链表头删数据
 void LBdelete(LBbiao**att) {
	 assert(*att != NULL);
	 assert(att != NULL);//断言
	 LBbiao* ps = *att;
	 *att = ps->next;
	 ps->next = NULL;
	 free(ps);
 }

 //链表尾删数据
 void LBweishan(LBbiao**att) {
	 assert(att != NULL);//断言
	 assert(*att != NULL);

	 LBbiao* ps = *att;
	 if (ps->next == NULL) {
		 *att = NULL;
		 free(ps);
	 }
	 else {
		 while (ps->next->next != NULL) {
			 ps = ps->next;
		 }
		 free(ps->next);
		 ps->next = NULL;
	 }
 }

 //链表尾插数据
 void LBweicha(LBbiao** att,CMMlet n) {
	 LBbiao* ps = *att;
	 LBbiao* pt = LBopen();
	 if (ps == NULL) {
		 *att = pt;
	 }
	 else {
		 while (ps->next != NULL) {
			 ps = ps->next;
		 }
		 ps->next = pt;
	 }
	 pt->SZ = n;
 }
 //链表查找
 LBbiao* LBchazhao(LBbiao* att, CMMlet n) {
	 assert(att);
	 LBbiao* ps = att;
	 while (ps != NULL) {
		 if (ps->SZ == n) {
			 return ps;
			 break;
		 }
		 ps = ps->next;
	 }
	 return NULL;
 }




 //链表在pos位置后插入x

 void LBporcr(LBbiao* pos, CMMlet x) {
	 assert(pos);
	 LBbiao* ps = pos;
	 LBbiao* pt = LBopen();
	 if (pos->next == NULL) {//相当于尾插数据
		 LBweicha(&pos, x);
	 }
	 else {
		//一般情况
		 pt->next=ps->next;
		 ps->next = pt;
		 pt->SZ = x;
	 }

 }


 //链表在pos位置后删除x
 void LBpossc(LBbiao* pos) {
	 assert(pos&&pos->next!=NULL);
	 if (pos->next->next == NULL) {//相当于尾删
		 free(pos->next);
		 pos->next = NULL;
	 }
	 else {
		 LBbiao* ps = pos->next;
		 pos->next = pos->next->next;
		 ps->next = NULL;
		 free(ps->next);

	 }
 }



 //测试链表1
 void ceshi1() {
	 LBbiao* arr = NULL;
	 LBweicha(&arr, 1);
	 LBweicha(&arr, 2);
	 LBweicha(&arr, 3);
	 LBweicha(&arr, 4);
	 LBexport(arr);
	 LBporcr(arr, 0);
	 LBexport(arr);
	 LBpossc(arr);
	 LBexport(arr);


 }

 int main() {
	 ceshi1();


	 return 0;
 }

  以上就是全部内容了,如果有错误或者不足的地方欢迎大家给予建议。 

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

java-springboot实现图片的上传

我们在resources目录下创建image目录来存放上传的图片 service层懒的写&#xff0c;就都写controller层了。 RestController RequestMapping("/upload") public class upload {PostMapping("/pic")public String upLoad(RequestParam("multipartFile…

EPSON推出的实时时钟模块RX8130CE功耗低至300nA、从容应对各种使用场景

随着科技的进步和消费者需求的不断变化&#xff0c;笔记本电脑市场继续展现出强劲的发展势头一方面移动性和轻薄性成为主流&#xff0c;另外一方面性能在不断提升&#xff0c;功能也日益丰富。实时时钟模组&#xff0c;作为提供时间和定时功能的单元模块&#xff0c;是笔记本电…

esp单片机下arduino_gfx不相干显示驱动优化对flash空间的占用对比

一般情况下&#xff0c;很多esp32或者esp8266下的tft模块驱动都会包含很多种&#xff0c;而我们只需要其中一种&#xff0c;那就有个疑问这些被编译进的显示驱动到底占用了多少空间&#xff0c;是否需要把他优化掉&#xff1f; 这是默认的驱动列表&#xff1a; 84个文件&…

“选项按钮”的妙用

背景&#xff1a;是否厌倦了下拉菜单&#xff1f;现在可以使用更好玩的选项按钮了。 操作&#xff1a;点击“开发工具”&#xff0c;插入“选项按钮”的窗体控件。 插入一个选项按钮以后&#xff0c;右键“设置控件格式”&#xff0c;设定单元格链接&#xff0c;比如说本次设定…

投影变形的在线查看工具

投影变形的在线查看工具 地图投影是将地球椭球面转换到平面上的过程。不同的地图投影方式会导致不同类型和程度的变形。如何去了解这种变形&#xff1f; ESRI开发过一个投影变换工具&#xff0c;可以在线展示各个投影坐标系的变形情况。通过选择data-wkid&#xff0c;可以在网…

k8s系列之十七 Istio中的服务治理

删除前面配置的目的地规则 [rootk8s-master ~]# kubectl delete destinationrule details destinationrule.networking.istio.io "details" deleted [rootk8s-master ~]# kubectl delete destinationrule productpage destinationrule.networking.istio.io "pr…

掌握ES6的箭头函数:深入了解其实用性与规则

引言 ES6&#xff08;ECMAScript 2015&#xff09;引入了箭头函数&#xff0c;这是一种新的函数声明方式&#xff0c;它改变了我们编写JavaScript代码的方式。箭头函数提供了更简洁、更直观的语法&#xff0c;并且具有一些独特的特性和行为。本文将深入探讨箭头函数的规则、用…

常见sql面试题

昨天朋友发来一个面试题&#xff0c;心血来潮自己写了下&#xff0c;废话不多说&#xff0c;直接上图和答案 这里是2张表&#xff0c;A表studenta&#xff0c;学号student&#xff0c;name姓名&#xff0c;年龄age B表scoreb 流水号id &#xff0c;课程course&#xff0c;学号…

学点儿Java_Day11_异常

1 异常概念、异常分类 ArrayIndexOutofBoundsException 数组下标越界异常 NullPointerException 空指针异常 StringIndexOutofBoundsException 字符串下标越界异常 ArithmeticException 算数异常/0 ClassCastException没法强制转换 大部分以able结尾的一般都是接口&#xff0…

Docker安装配置

1. 安装docker-ce sudo yum-config-manager --add-repo http://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo yum -y install docker-ce sudo systemctl enable docker 2. 设置代理 参照&#xff1a;https://docs.docker.com/config/daemon/systemd/#httpht…

计算机网络:物理层 - 编码与调制

计算机网络&#xff1a;物理层 - 编码与调制 基本概念编码不归零制编码归零制编码曼彻斯特编码差分曼彻斯特编码 调制调幅调频调相混合调制 基本概念 在计算机网络中&#xff0c;计算机需要处理和传输用户的文字、图片、音频和视频&#xff0c;他们可以统称为消息数据&#xf…

【JavaScript】面试手撕深拷贝

&#x1f308;个人主页: 鑫宝Code &#x1f525;热门专栏: 闲话杂谈&#xff5c; 炫酷HTML | JavaScript基础 ​&#x1f4ab;个人格言: "如无必要&#xff0c;勿增实体" 文章目录 引入深拷贝的作用深浅拷贝的区别浅拷贝深拷贝 深拷贝实现方式JSON.parse(JSON.s…

求两个单链表的差集

归纳编程学习的感悟&#xff0c; 记录奋斗路上的点滴&#xff0c; 希望能帮到一样刻苦的你&#xff01; 如有不足欢迎指正&#xff01; 共同学习交流&#xff01; &#x1f30e;欢迎各位→点赞 &#x1f44d; 收藏⭐ 留言​&#x1f4dd; 但行前路&#xff0c;不负韶华&#…

基于SSM作业提交与批改

基于SSM作业提交与批改的设计与实现 摘要 社会的进步导致人们对于学习的追求永不止境&#xff0c;那么追求学习的方式也从单一的书本教程变成了多样化的学习方式。多样化的学习方式不仅仅是需要人们智慧的依靠&#xff0c;还需要能够通过软件的加持进行信息化的价值体现。软件…

VMware下建立CentOS 7

1.点击新建虚拟机 2.下一步 3.选择号安装程序光盘映像文件位置&#xff0c;下一步 4.选择版本和操作系统然后下一步 5.编辑虚拟机名称并选择安装位置&#xff0c;然后下一步 6.设置最大磁盘大小&#xff0c;下一步 7.点击完成 8.点击编辑虚拟机设置 9.将此虚拟机内存设置为2G&a…

DevSecOps平台架构系列-亚马逊云AWS DevSecOps平台架构

目录 一、概述 二、AWS DevSecOps实施原则 2.1 尽早采用安全测试&#xff0c;加速问题反馈 2.2 优先考虑预防性安全控制 2.3 部署检测性安全控制时&#xff0c;确保有与之互补的响应性安全控制 2.4 安全自动化 2.5 总结 三、AWS DevSecOps关键组件 3.1 关键组件 3.2 关…

Div2 D. Effects of Anti Pimples

解题思路 将由小到大排序若不考虑绿色的情况则为最大值的情况为&#xff0c;即选择在它之前的点对于同时选,会被统计贡献时考虑考虑绿色&#xff0c;对于每个&#xff0c;若选则均选对于每个预处理出&#xff0c;记作对由小到大排序为答案的情况为 …

Codigger用户篇:安全、稳定、高效的运行环境(一)

在当今数字化时代&#xff0c;个人数据的安全与隐私保护显得尤为重要。为了满足用户对数据信息的安全需求&#xff0c;我们推出Codigger分布式操作系统&#xff0c;它提供了一个运行私有应用程序的平台&#xff0c;旨在为用户提供一个安全、稳定、高效的私人应用运行环境。Codi…

基于Weibull、Beta、Normal分布的风、光、负荷场景生成及K-means场景削减方法

目录 一、主要内容&#xff1a; 二、代码运行效果&#xff1a; 三、Weibull分布与风机风速&#xff1a; 四、Beta分布与光伏辐照度&#xff1a; 五、Normal分布与电负荷&#xff1a; 六、K-means聚类算法&#xff1a; 七、完整代码数据下载&#xff1a; 一、主要内容&am…

STM32技术打造:智能考勤打卡系统 | 刷卡式上下班签到自动化解决方案

文章目录 一、简易刷卡式打卡考勤系统&#xff08;一&#xff09;功能简介原理图设计程序设计 哔哩哔哩&#xff1a; https://www.bilibili.com/video/BV1NZ421Y79W/?spm_id_from333.999.0.0&vd_sourcee5082ef80535e952b2a4301746491be0 一、简易刷卡式打卡考勤系统 &…