DS线性表之单链表的讲解和实现(2)

文章目录

  • 前言
  • 一、链表的概念
  • 二、链表的分类
  • 三、链表的结构
  • 四、前置知识准备
  • 五、单链表的模拟实现
    • 定义头节点
    • 初始化单链表
    • 销毁单链表
    • 打印单链表
    • 申请节点
    • 头插数据
    • 尾插数据
    • 头删数据
    • 尾删数据
    • 查询数据
    • 在pos位置之后插入数据
    • 删除pos位置之后的数据
  • 总结


前言

  本篇的单链表完全来说是单向不带头单链表,这种和另外一种链表(双向带头循环链表)使用最广泛,我们只要对它们两个有所了解即可

  正文开始!


一、链表的概念

  链表是一种物理存储结构上非连续、非顺序的存储结构,数据元素的逻辑顺序是通过链表中的指针链接次序实现的

二、链表的分类

在这里插入图片描述

三、链表的结构

  就如同下图一样,一节一节,前面连着后面就是链表的一种表现
在这里插入图片描述
在这里插入图片描述
同时我们也发现以下几点:

  1. 从上图可看出,链式结构在逻辑上是连续的,但是在物理上不一定连续
  2. 节点一般都是在上申请出来的
  3. 从堆上申请空间,两次申请得到的内存可能连续,也可能不连续

物理结构:数据实际存储在内存中的结构;
逻辑结构:想象出来的结构

四、前置知识准备

在正式开始实现之前,我希望你有以下认识:

  1. 实现部分接口需要通过二级指针接受实参:原因在于我们需要可以修改实参(而形参只是实参的一份拷贝,要想修改实参,必须得传指针,同样若实参本来就是指针,那么就要传指针的指针,即二级指针),而是实参为一级指针时(同样是传递地址),需要使用二级指针进行接受,否则获得临时拷贝,不会影响到实参。修改实参的情况,比如一开始为空,在插入时需将头指针存储在有效结点的的地址上,需要改变实参的值
  2. 单链表的初始化:这里实现链表,没有必要进行初始化,初始化对于一开始就要开辟的空间有初始化的需求,表示多个节点通过地址链接在一起,那么只需要开辟新节点的时候,初始化下就行了(有哨兵位需要初始化)
  3. 二级指针断言:二级指针存放的是头节点的地址,头节点的地址为空,那么还有什么意义呢?

五、单链表的模拟实现

定义头节点

//单链表节点
//根据定义需要存储一个数据和一个指向下一个结点的指针
typedef int SLDataType;//定义数据类型,可以根据需要更改,typedef一下就很方便
typedef struct SList
{
	SLDataType data;    //数据域 存储数据
	struct SList* next; //指针域 存储指向下一个结点的指针
}SList;

请注意,这里不能在节点内就单独用SList来定义next指针,因为这时候还没有typedef成功呢,还在节点内部

初始化单链表

因为创建一个变量实质是给变量开辟一块内存空间,但是这块内存空间可能有遗留的数据,所以在创建变量之后需要进行初始化,而数据域我们也不清楚到底该传那个,随便传个0就行,但是指针域就必须置空了,否则就是野指针

void SListInit(SList* phead)
{
	assert(phead);     //防止传入空指针,传入则报错
	phead->data = 0;   //将数据初始化为0
	phead->next = NULL;//将结点指针初始化为NULL
}

销毁单链表

有开辟内存,自然而然的就有还回内存,即销毁单链表

在这里插入图片描述

void SListDestroy(SList* phead)
{
	assert(phead);
	SList* cur = phead; //为了能在后序找到头结点,所以新创建一个变量指向头结点
	while (cur != NULL)
	{
		SList* next = cur->next;
		free(cur);
		cur = next;
	}
}

我们不妨来想想为什么是传一级指针即可,首先,我们这里是为了销毁内存,虽然说形参的这个节点指针和实参的节点指针地址不一样,但是所存的节点都是同一个节点!,所以可以直接传指针,有因为我们说链表在物理上是不连续的,所以得通过指针跳着跳着一个个销毁

打印单链表

在这里插入图片描述
同样的,我们只要传一级指针就可以了,且通过指针来跳转

void SListPrint(SList* phead)
{
	assert(phead);
	SList* cur = phead;
	while (cur != NULL)
	{
		printf("%d->", cur->data);
		cur = cur->next;
	}
	printf("NULL\n");
}

申请节点

在插入中使用相当频繁,所以我们再对此单独实现一个函数,使得代码更加的结构化
在这里插入图片描述
请注意,指针并不单独开空间,它起到的更多是一个中间人的作用,通过指针来控制

SList* BuySeqList(SLDataType x)
{
	SList* pnewNode = (SList*)malloc(sizeof(SList));//动态开辟一个单链表类型大小
	if (pnewNode == NULL)//动态开辟的内存不一定成功,所以需要判断
	{
		printf("malloc fail\n");
		exit(-1);
	}
	
	//内存开辟成功则把数据赋值到指定位置
	pnewNode->data = x;
	pnewNode->next = NULL;

	return newnode;
}

头插数据

在这里插入图片描述

void SListPushFront(SList** pphead, SLDataType x)
{
	SList* newnode = BuySeqList(x);
	newnode->next = *pphead;
	*pphead = newnode;
}

尾插数据

在这里插入图片描述

void SListPushBack(SList** pphead, SLDataType x)
{
	SList* newnode = BuySeqList(x);
	if (*pphead == NULL) // 没有节点的时候
	{
		*pphead = newnode;
	}
	else // 有节点的时候
	{
		SList* tail = *pphead;
		while (tail->next != NULL)
		{
			tail = tail->next;
		}
		tail->next = newnode;
	}
}

头删数据

void SListPopFront(SList** pphead)
{
	assert(*pphead);//头结点不为空则有数据
	SList* head = (*pphead)->next;
	free(*pphead);
	*pphead = head;
}

在这里插入图片描述

尾删数据

void SListPopBack(SList** pphead)
{
	assert(*pphead);
	if ((*pphead)->next == NULL)
	{
		free(*pphead);
		*pphead = NULL;
	}
	else
	{
		SList* tail = *pphead;
		SList* prev = NULL;
		while (tail->next != NULL)
		{
			prev = tail;
			tail = tail->next;
		}
		free(tail);
		tail = NULL;
		prev->next = NULL;
	}
}

在这里插入图片描述

查询数据

找到则返回当前当前数据结点地址,没找到则返回空地址
在这里插入图片描述

SList* SListFind(SList* phead, SLDataType x)
{
	assert(phead);
	SList* cur = phead;
	while (cur != NULL)
	{
		if (cur->data == x)
		{
			return cur;
		}
		cur = cur->next;
	}
	return NULL;
}

在pos位置之后插入数据

在这里插入图片描述

void SListInsertAfter(SList* pos, SLDataType x)
{
	assert(pos);
	SList* newnode = BuySeqList(x);
	SList* next = pos->next;
	pos->next = newnode;
	newnode->next = next;
}

删除pos位置之后的数据

在这里插入图片描述

void SListEraseAfter(SList* pos)
{
	assert(pos);
	assert(pos->next);//判断pos之后是否有数据,没数据则报错
	SList* next = pos->next->next;
	free(pos->next);
	pos->next = next;
}

总结

  链表可以说是CS学生遇到的第二个劝退点了(第一个是指针),它是我们所学知识的一个集成展现,需要我们对前面知识的充分掌握,所以对计算机来说,知识比较连贯,这也是学习它比较难受的地方

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

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

相关文章

高性能计算应用优化实践之VASP

VASP简介 VASP是最常见的第一性原理计算软件之一。第一性原理计算是基于密度泛函理论,通过选择合适的交换关联赝势(GGA或LDA),然后基于迭代方法自洽求解Kohn-Sham方程,直到所求出的新的电荷密度与输入的电荷密度在收敛判据范围内&#xff0c…

Python酷库之旅-第三方库Pandas(145)

目录 一、用法精讲 656、pandas.Timestamp.resolution属性 656-1、语法 656-2、参数 656-3、功能 656-4、返回值 656-5、说明 656-6、用法 656-6-1、数据准备 656-6-2、代码示例 656-6-3、结果输出 657、pandas.Timestamp.second属性 657-1、语法 657-2、参数 6…

JAVA开发中SpringMVC框架的使用及常见的404问题原因以及SpringMVC框架基于注解的开发实例

一、JAVA开发中SpringMVC框架的使用及常见的404问题原因 使用SpringMVC建立一个web项目,在IDEA中file->new->project建立一个空项目project。不用选择create from archetype从模板创建。然后在项目的pom.xml中添加公共的依赖包括org.springframework&#xff…

YOLO11改进|卷积篇|引入空间通道重组卷积ScConv

目录 一、【SCConv】卷积1.1【SCConv】卷积介绍1.2【SCConv】核心代码 二、添加【SCConv】卷积2.1STEP12.2STEP22.3STEP32.4STEP4 三、yaml文件与运行3.1yaml文件3.2运行成功截图 一、【SCConv】卷积 1.1【SCConv】卷积介绍 SCConv 模块提供了一种新的视角来看待CNNs的特征提取…

如何通过钢筋计来优化施工安全

在现代建筑工程中,施工安全一直是首要关注的问题。特别是在高层建筑、桥梁和地下工程等复杂结构中,确保钢筋的正确安装和稳定性能,直接关系到工程的整体安全性和耐久性。钢筋计作为一种专门用于测量和监测钢筋应力和应变的设备,其…

4.人员管理模块(开始预备工作)——帝可得管理系统

目录 前言一、需求分析1.页面原型2.创建SQL 二、使用若依框架生成前后端代码1.添加目录菜单2.添加数据字典3.配置代码生成信息4.下载代码并导入项目5.快速导入方法 三、 总结 前言 提示:本篇讲解人员管理模块的开发的预备工作,包括需求分析、生成代码、…

DockerCompose 启动 open-match

背景介绍 open-match是Google和unity联合开源的支持实时多人匹配的框架,已有多家游戏厂商在生产环境使用,官网 https://open-match.dev/site/ 。原本我们使用的是UOS上提供的匹配能力,但是UOS目前不支持自建的Dedicated servers 集群&#x…

grpc的python使用

RPC 什么是 RPC ? RPC(Remote Procedure Call)远程过程调用,是一种计算机通信协议,允许一个程序(客户端)通过网络向另一个程序(服务器)请求服务,而无需了解…

【Matlab】Matlab 导入数据.csv或者.xlsx文件,然后使用这些数据来绘制图表

Matlab 导入数据.csv或者.xlsx文件,然后使用这些数据来绘制图表 初始数据 filename C:\Users\jia\Desktop\yadian\data\1Hz 2024_09_12 17_10_06.csv; 代码: clc;clear close all; % 读取Excel文件 filename C:\Users\jia\Desktop\yadian\data\1Hz …

【EXCEL数据处理】保姆级教程 000016案例 EXCEL的vlookup函数。

【EXCEL数据处理】000016案例 vlookup函数。 前言:哈喽,大家好,今天给大家分享一篇文章!创作不易,如果能帮助到大家或者给大家一些灵感和启发,欢迎收藏关注哦 💕 目录 【EXCEL数据处理】保姆级教…

Flutter包管理(三)

1、作用 在APP的实际开发过程中往往会依赖很多包,而这些包之间存在着交叉依赖、版本依赖,由开发者自己管理手动管理会非常麻烦,每种开发生态或编程官方会提供一些包的管理工具,在Flutter中我们在pubspec.yaml文件中来管理第三方依…

机器学习/数据分析--用通俗语言讲解时间序列自回归(AR)模型,并用其预测天气,拟合度98%+

时间序列在回归预测的领域的重要性,不言而喻,在数学建模中使用及其频繁,但是你真的了解ARIMA、AR、MA么?ACF图你会看么?? 时间序列数据如何构造???,我打过不少…

GS-SLAM论文阅读笔记-MGSO

前言 MGSO首字母缩略词是直接稀疏里程计(DSO),我们建立的光度SLAM系统和高斯飞溅(GS)的混合。这应该是第一个前端用DSO的高斯SLAM,不知道这个系统的组合能不能打得过ORB-SLAM3,以及对DSO会做出怎么样的改进以适应高斯地图,接下来…

Android一个APP里面最少有几个线程

Android一个APP里面最少有几个线程 参考 https://www.jianshu.com/p/92bff8d6282f https://www.jianshu.com/p/8a820d93c6aa 线程查看 Android一个进程里面最少包含5个线程,分别为: main线程(主线程)FinalizerDaemon线程 终结者守护线程…

Golang | Leetcode Golang题解之第462题最小操作次数使数组元素相等II

题目&#xff1a; 题解&#xff1a; func partition(a []int, l, r int) int {x : a[r]i : l - 1for j : l; j < r; j {if a[j] < x {ia[i], a[j] a[j], a[i]}}a[i1], a[r] a[r], a[i1]return i 1 }func randomPartition(a []int, l, r int) int {i : rand.Intn(r-l1…

Android车载——VehicleHal运行流程(Android 11)

1 概述 本篇主要讲解VehicleHal的主要运行流程&#xff0c;包括设置属性、获取属性、订阅属性、取消订阅、持续上报属性订阅等。 2 获取属性流程 2.1 获取属性流程源码分析 作为服务注册到hwServiceManager中的类是VehicleHalManager&#xff0c;所以&#xff0c;CarServic…

【Qt】控件概述(2)—— 按钮类控件

控件概述&#xff08;2&#xff09; 1. PushButton2. RadioButton——单选按钮2.1 使用2.2 区分信号 clicked&#xff0c;clicked(bool)&#xff0c;pressed&#xff0c;released&#xff0c;toggled(bool)2.3 QButtonGroup分组 3. CheckBox——复选按钮 1. PushButton QPushB…

简单粗暴理解GNN、GCN、GAT

GNN 思想&#xff1a;近朱者赤近墨者黑 GNN的流程&#xff1a; 聚合&#xff08;把邻居的信息贴到自己身上来&#xff0c;作为它自己特征的补足&#xff09;更新循环&#xff08;为什么要多次&#xff1f;看以下例子&#xff09; GNN能干嘛&#xff1f; 1.结点分类&#xf…

动态规划lc

先找到规律&#xff0c;然后找边界情况&#xff1b;部分特殊情况分类讨论 *递归 70.爬楼梯 简单 提示 假设你正在爬楼梯。需要 n 阶你才能到达楼顶。 每次你可以爬 1 或 2 个台阶。你有多少种不同的方法可以爬到楼顶呢&#xff1f; 示例 1&#xff1a; 输入&#xff1a…

基于yolov8、yolov5的PCB板缺陷检测系统(含UI界面、数据集、训练好的模型、Python代码)

blog.csdnimg.cn/direct/6f53422ed9fd44dc8daad6dc5481c4c9.png) 项目介绍 项目中所用到的算法模型和数据集等信息如下&#xff1a; 算法模型&#xff1a;     yolov8、yolov8 SE注意力机制 或 yolov5、yolov5 SE注意力机制 &#xff0c; 直接提供最少两个训练好的模型…