速学数据结构 | 手把手教你会单链表的构建方式

news2025/1/21 12:09:12

在这里插入图片描述

🎬 鸽芷咕:个人主页

 🔥 个人专栏: 《初阶数据结构》《C语言进阶篇》

⛺️生活的理想,就是为了理想的生活!

文章目录

  • 📋 前言
  • 1. 什么是链表
    • 1.1 链表的物理结构
    • 1.2 链表的种类
  • 2. 链表的实现
    • 一. SList.h 单链表的声明
      • 3.1 定义链表结构
      • 3.2 单链表函数的声明
    • 二. SList.h 单链表的定义
      • 2.1 动态申请链表一个节点
      • 2.2 单链表打印
      • 2.3 单链表尾插
      • 2.4 单链表的头插
      • 2.5 单链表的尾删
      • 2.6 单链表头删
      • 2.7 单链表查找
      • 2.8 在pos之前插入x
      • 2.9 在pos之后插入x
      • 2.10 删除pos位置
      • 2.11 删除pos的后一个位置
    • 三. test.c 单链表的功能测试
  • 📝全篇总结

📋 前言

  🌈hello! 各位宝子们大家好啊!今天给大家带来的是初阶数据结构中单链表的构建方式,手把手教会你单链表
  ⛳️链表是指一种逻辑上是连在一起的数据结构,但是物理存储上却是分开的部分!是通过链表中的指针链接次序实现的一种数据结构!
  📚本期文章收录在《初阶数据结构》,大家有兴趣可以看看呐
  ⛺️ 欢迎铁汁们 ✔️ 点赞 👍 收藏 ⭐留言 📝!

1. 什么是链表

概念:链表是一种物理存储结构上非连续、非顺序的存储结构,数据元素的逻辑顺序是通过链表中的指针链接次序实现的 。
在这里插入图片描述

顺序表我们都知道有点类似数组,是在物理上一块连续存放的内存块!所以顺序表也叫 线性表 但是开辟必须需要连续的空间空间的浪费特别严重!

  • 所以就有链表这种数据结构,避免了空间的浪费。
  • 链表在逻辑上是连在一起但是内存块确实分布在不同位置的
  • 通过指针访问每个链表的节点

1.1 链表的物理结构

在这里插入图片描述
从这里可以看出:

   链表在逻辑上是连续的但是,物理上是单独分开的。由每一块的指针记录下一个节点的地址进行访问

🔥 注意

  • 现实中的节点一般都是在堆上申请出来的
  • 在堆上申请的空间,是编译器按照一定规则分配的,俩次申请的空间可能有时连续有时不连续。

1.2 链表的种类

实际中链表的结构非常多样,以下情况组合起来就有8种链表结构:

  • 单向或者双向
    在这里插入图片描述

  • 带头或者不带头
    在这里插入图片描述

  • 循环或者非循环
    在这里插入图片描述

但是我们今天就先从简单的入手,先来实现一下单链表的结构!先从简单的下手。

  • 单链表结构简单,一般不会单独用来存数据。实际中更多是作为其他数据结构的子结构,如哈希桶、图的邻接表等等。另外这种结构在笔试面试中出现很多。

2. 链表的实现

好了废话不多讲,下面我们就来到链表的实现过程。链表前面我们了解了不是一个连续的存储结构,S是利用指针来进行每个节点的访问。

  • 注:我们把链表里的每个内容叫做一个节点

在这里插入图片描述

一. SList.h 单链表的声明

3.1 定义链表结构

链表链表首先我们要先定义链表的结构,链表既有数据又要和下一个链表联系起来那么肯定是要使用结构体:

  • 来定义链表
  • 而内容需要一个 data 和 指向下一个节点的指针!
typedef  int  SLTDataType;
//定义链表结构
typedef	struct SListNode
{
	SLTDataType data;
	struct SListNode* next;
}SLTNode;

3.2 单链表函数的声明

单链表要实现的功能其实也非常简单,和顺序表是一模一样的。增删改查等这些操作而我们只要把这些函数实现了,那么在刷关于链表的题的时候也就无非是这些操作的变形。

  • 下面我们就来看看单链表具体要实现的功能把!
//动态申请链表节点
SLTNode* BuySListNode(SLTDataType x);

//打印单链表
void SLTPrint(SLTNode* plist);

//单链表头插
void SLTPushFront(SLTNode** pplist,SLTDataType x);

//单链表尾插
void SLTPushBack(SLTNode** pplist, SLTDataType x);

//单链表头删
void SLTPopFront(SLTNode** pplist);

//单链表尾删
void SLTPopBack(SLTNode** pplist);

//查找节点
SLTNode* SLTFind(SLTNode* pplist, SLTDataType x);

//在pos以前插入x
void SLTInsert(SLTNode** pplist, SLTNode* pos, SLTDataType x);

// 在pos以后插入x
void SLTInsertAfter(SLTNode* pos, SLTDataType x);

// 删除pos位置
void SLTErase(SLTNode** pplist, SLTNode* pos);

// 删除pos的后一个位置
void SLTEraseAfter(SLTNode* pos);

// 单链表的销毁
void SListDestroy(SLTNode* pplist);

二. SList.h 单链表的定义

2.1 动态申请链表一个节点

前面我们把链表的基本结构定义好了那么接下来就申请节点了,这样我们才能进行链表的链接已经数据的存储!

  • 而开辟节点直接用 malloc 开辟空间然后赋值就好啦!
//动态申请链表节点
SLTNode* BuySListNode(SLTDataType x)
{
	SLTNode* newnode = (SLTNode*)malloc(sizeof(SLTNode));
	if (newnode == NULL)
	{
		perror("malloc file");
		exit(-1);
	}

	newnode->data = x;
	newnode->next = NULL;

	return newnode;
}

2.2 单链表打印

打印单链表也是一个十分简单的功能了,既然我们链表的每个节点的 next 存储的都是下一个节点那么直接循环访问就好啦!

  • 要注意控制好循环变量的结束条件
  • 和链表最后 NULL 的打印
//打印单链表
void SLTPrint(SLTNode* plist)
{
	SLTNode* cur = plist;
	while (cur)
	{
		printf("%d->", cur->data);
		cur = cur->next;
	}

	printf("NULL\n");
}

2.3 单链表尾插

单链表的尾插也不是很难控制好着俩点就好了:

  • 第一个是 plist 为空的情况,直接尾插
  • 第二个是 plist 不为空的情况,循环找到尾直接尾插
//单链表尾插
void SLTPushBack(SLTNode** pplist, SLTDataType x)
{
	SLTNode* newnode = BuySListNode(x);
	
	if (*pplist == NULL)
	{
		*pplist = newnode;
	}
	else
	{
		SLTNode* tail = *pplist;
		while (tail->next != NULL)
		{
			tail = tail->next;
		}

		tail->next = newnode;
	}
}

2.4 单链表的头插

这个可以说是最简单的部分了,只要让要 plist 指向 插入的节点,插入节点的 next 指向下一个节点。

  • 还要注意一下断言
//单链表头插
void SLTPushFront(SLTNode** pplist,SLTDataType x)
{
	SLTNode* newnode = BuySListNode(x);

	newnode->next = *pplist;
	*pplist = newnode;
}

2.5 单链表的尾删

单链表的头插尾插我们实现了下面就是单链表的尾删了。注意要控制好边界的几种情况就好了

  • 一个是链表只有一个节点的情况下直接删除
  • 还有一个是链表有多个节点需要遍历
//单链表尾删
void SLTPopBack(SLTNode** pplist)
{
	//非空判断
	assert(*pplist);
	SLTNode* tail = *pplist;
	
	
	if ((*pplist)->next == NULL)
	{
		free(*pplist);
		*pplist = NULL;
	}
	else
	{
		while (tail->next->next != NULL)
		{
			tail = tail->next;
		}

	 
		free(tail->next);
		tail->next = NULL;
	}

}

2.6 单链表头删

单链表的头删注意考虑俩总情况,一个是只有一个节点释放,一个是多个节点释放。

  • 在 assert 断言一下NULL链表就不删除
//单链表头删
void SLTPopFront(SLTNode** pplist)
{
	//非空判断
	assert(*pplist);

	SLTNode* newhead = (*pplist)->next;
	free(*pplist);
	*pplist = newhead;
}

2.7 单链表查找

为什么要先写单链表的查找呢,因为我们现实中通常不知道我们要删除或者插入的数在第一个点上所以需要先查找要删除或者插入的数到时候删除直接复用就好了。

  • 查找的话直接循环遍历就好了,如果找不到就返回空NULL。
//查找节点
SLTNode* SLTFind(SLTNode* plist, SLTDataType x)
{
	SLTNode* cur = plist;
	while (cur)
	{
		if (cur->data == x)
		{
			return cur;
		}
		cur = cur->next;
	}

	return NULL;
}

2.8 在pos之前插入x

和前面的删除一样,需要控制好不同的情况和 暴力检测一下 plist 传过来的是不是一个空指针。

  • 空指针就直接退出说明传错了
  • 还要注意遍历pos前一个节点
//在pos以前插入x
void SLTInsert(SLTNode** pplist, SLTNode* pos, SLTDataType x)
{
	assert(*pplist);
	assert(pos);
	

	if (pos == *pplist)
	{
		SLTPushFront(pplist,x);
	}
	else
	{
		SLTNode* prev = *pplist;

		while (prev->next != pos)
		{
			prev = prev->next;
		}
		SLTNode* newnode = BuySListNode(x);

		prev->next = newnode;
		newnode->next = pos;
	}
}

2.9 在pos之后插入x

这个就没在pos之前插入x那么复杂了可以根据指针找到下一个节点,然后删除pos的后一个节点

  • 将next 连接到pos的下下个节点上
// 在pos以后插入x
void SLTInsertAfter(SLTNode* pos, SLTDataType x)
{
	SLTNode* newnode = BuySListNode(x);

	if (pos->next == NULL)
	{
		pos->next = newnode;
	}
	else
	{
		SLTNode* next = pos->next->next;
		pos->next = newnode;
		newnode->next = next;
	}
}

2.10 删除pos位置

删除pos的位置就需要循环遍历pos,的前一个位置。然后进行 free 释放空间

  • 在把俩个节点关联起来就可以了

// 删除pos位置
void SLTErase(SLTNode** pplist, SLTNode* pos)
{
	assert(pplist);
	assert(pos);

	if(*pplist == pos)
	{
		SLTPopFront(pplist);
	}
	else
	{
		SLTNode* prve = *pplist;
		while (prve->next != NULL)
		{
			prve = prve->next;
		}

		prve->next = pos->next;
		free(pos);
	}
}

2.11 删除pos的后一个位置

// 删除pos的后一个位置
void SLTEraseAfter(SLTNode* pos)
{
	assert(pos);

	assert(pos->next);
	
	SLTNode* posNext = pos->next;
	pos->next = posNext->next;
	free(posNext);
	posNext = NULL;

	
}

三. test.c 单链表的功能测试

这里博主就不给大家测试给大家写个样例,大家自己去试试增删查改哦!

void Test_SList2()
{
	SLTNode* plist = NULL;

	SLTPushFront(&plist, 1);
	SLTPushFront(&plist, 2);
	SLTPushFront(&plist, 3);
	SLTPushFront(&plist, 4);
	SLTPushFront(&plist, 5);
	SLTPrint(plist);
}
int main()
{
	Test_SLsit1();
	
}

📝全篇总结

✅ 归纳:
好了以上就是关于分支语句 链表 的所有知识点了,大家快下去练习练习吧!
  链表的介绍
  链表的结构
  链表的增删查改

☁️ 把本章的内容全部掌握,铁汁们就可以熟练应用switch语句啦!
看到这里了还不给博主扣个:
⛳️ 点赞☀️收藏 ⭐️ 关注

💛 💙 💜 ❤️ 💚💓 💗 💕 💞 💘 💖
拜托拜托这个真的很重要!
你们的点赞就是博主更新最大的动力!
有问题可以评论或者私信呢秒回哦。
在这里插入图片描述

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

10. 激光雷达到车身坐标系外参的标定方法(lidar2car)

目录 0. 论文及代码1. 标定原理2. 拟合平面3. 标定roll/pitch/height4. 标定yaw4.1 理解从B_spline拟合的轨迹中得到vehicle航向 5. 精度 0. 论文及代码 参考论文:SensorX2car: Sensors-to-car calibration for autonomous driving in road scenarios 参考代码&…

kafka初体验基础认知部署

kafka 基础介绍 Apache Kafka是一个分布式流处理平台,最初由LinkedIn开发并于2011年开源。它主要用于解决大规模数据的实时流式处理和数据管道问题。 Kafka是一个分布式的发布-订阅消息系统,可以快速地处理高吞吐量的数据流,并将数据实时地分…

医院PACS系统源码 PACS系统源码

医用软件中的影像归档与传输系统软件(Picture Archiving and Communication System,简称PACS)是一种用于存储、管理和传输医学影像数据的系统。其主要功能包括: 影像存储:PACS可以将医学影像数据以数字化的形式存储在服…

YoloV8训练自己的模型 Pycharm Remote Development

参考视频:https://www.youtube.com/watch?vm9fH9OWn8YM YOLO官方网站:GitHub - ultralytics/ultralytics: NEW - YOLOv8 🚀 in PyTorch > ONNX > OpenVINO > CoreML > TFLite 在本地的pycharm上面建立一个项目 使用scp把代码传…

星球作业(第十一期)Android中Binder简述

Binder 什么是binder?简述下它的工作过程和使用场景。 什么是Binder? Binder是Android中的一个类,实现了IBinder接口; 从IPC的角度来说,Binder是Android中的一种通讯方式; 从Android Framework角度来说&a…

一文搞清楚Java中常见的IO模型

什么是IO 首先,我们要清楚什么是IO,根据冯诺依曼结构,计算机结构分为5部分:运算器、控制器、存储器、输入设备和输出设备。 输入设备和输出设备都属于外设,网卡、硬盘这种既可以属于输入设备也可以属于输出设备。 输入…

深度学习-卷积神经网络-ResNET

文章目录 前言1.resnet2.作者3.精度(TOP-5)4.论文一览5.竞赛排名6.网络退化7.残差8.残差 1.作者 前言 本文来自B站: ResNet深度残差网络 1.resnet 2.作者 3.精度(TOP-5) 4.论文一览 5.竞赛排名 6.网络退化 ResNet解…

拆解常见的6类爆款标题写作技巧!

究竟是先写好文章再拟标题还是先确定标题再写文章呢?很多写稿小白都会有这样的疑惑。 在“人人皆可新媒体”的时代,公众号推文类型琳琅满目,每个人都可以建立自己的公众号,写出自己想写的文章。 但怎样起标题、起什么样的标题&a…

MyCat安装文档

JDK安装 JDK具体安装步骤如下: 1. 上传安装包 使用FinalShell自带的上传工具将jdk的二进制发布包上传到Linux 由于上述在进行文件上传时,选择的上传目录为根目录 /,上传完毕后,我们执行指令 cd / 切换到根目录下,查…

STM32 10个工程篇:1.IAP远程升级(六)

在IAP远程升级的最后一篇博客里,笔者想概括性地梳理总结IAP程序设计中值得注意的问题,诚然市面上或者工作后存在不同版本的IAP下位机和上位机软件,也存在不同定义的报文格式,甚至对于相似的知识点不同教程又有着完全不同的解读&am…

You Know What is C++嵌套类

C嵌套类 一、嵌套类1.嵌套类和访问权限2.作用域3.访问控制 一、嵌套类 在一个类的内部定义另一个类,我们称之为嵌套类,或者嵌套类型。引入嵌套类,因为外围类需要使用嵌套类对象作为底层实现,并且该嵌套类只用于外围类的实现&…

黑马JVM总结(二十七)

(1)synchronized代码块 synchronized代码块的底层原理,它是给一个对象进行一个加锁操作,它是如何保证如果你出现了synchronized代码块中出现了问题,它需要给这个对象有一个正确的解锁操作呢,加锁解锁是成对…

为什么网络安全明明缺口很大,却看起来招聘很少呢?

2023 年我国网络空间安全人才数量缺口超过了 140 万,就业人数却只有 10 多万,缺口高达了 93%。这里就有人会问了: 1、网络安全行业为什么这么缺人? 2、明明人才那么稀缺,为什么招聘时招安全的人员却没有那么多呢&…

10、【Qlib】【主要组件】高频交易嵌套决策执行框架

10、【Qlib】【主要组件】高频交易嵌套决策执行框架 简介简介 日间交易(例如,投资组合管理)和当日交易(例如,订单执行)是量化投资中的两个热门话题,并且通常会分别进行研究。 为了获得日间和当日交易的联合交易绩效,它们必须相互作用,并共同进行回测。为了支持多级的…

7.Tensors For Beginneers - Convector Components

介绍协向量时,曾说过它们有点像 行向量, 行向量确实以某种方式代表了协向量, 这里说明一下: 协向量是不变的; 协向量组件是可变的。 协向量不依赖坐标系,协向量的组件取决于坐标系。 当我们说协向量具有组…

基于SSM的旅游攻略网站设计与实现

末尾获取源码 开发语言:Java Java开发工具:JDK1.8 后端框架:SSM 前端:采用JSP技术开发 数据库:MySQL5.7和Navicat管理工具结合 服务器:Tomcat8.5 开发软件:IDEA / Eclipse 是否Maven项目&#x…

【SpringCloud】微服务技术栈入门3 - Gateway快速上手

目录 GatewayWebFlux网关基本配置过滤器与断言工厂全局过滤器跨域处理 CORS Gateway WebFlux gateway 基于 webflux 构建 WebFlux 是基于反应式流概念的响应式编程框架,用于构建异步非阻塞的 Web 应用程序。它支持响应式编程范式,并提供了一种响应式的方…

Java中阻塞队列原理、特点、适用场景

文章目录 阻塞队列对比、总览阻塞队列本质思想主要队列讲解ArrayBlockingQueueLinkedBlockingQueueSynchronousQueueLinkedTransferQueuePriorityBlockingQueueDelayQueueLinkedBlockingDeque 阻塞队列对比、总览 阻塞队列本质思想 阻塞队列都是线程安全的队列. 其最主要的功能…

3分钟基于Chat GPT完成工作中的小程序

1. 写在前面 GPT自从去年爆发以来,各大公司在大模型方面持续发力,行业大模型也如雨后春笋一般发展迅速,日常工作中比较多的应用场景还是问答模式,作为写程序的辅助也偶尔使用。今天看到一篇翻译的博客“我用 ChatGPT,…