单链表(无头单项非循环)

news2024/10/7 13:22:07

在这里插入图片描述

文章目录

  • 前言
  • 概述
  • 链表的实现
    • 初始化
    • 遍历单链表
    • 创建新节点
    • 尾插
    • 头插
    • 尾删
    • 头删
    • 单链表的查找
    • 在pos位置之前插入一个节点
    • 在pos位置删除节点
    • 在pos位置后插入节点
    • 删除pos后一个节点
    • 销毁
  • 结尾

前言

链表是一种物理存储结构上非连续、非顺序的存储结构,数据元素的逻辑顺序是通过链表中的指针链接次序实现的 。链表的形式有很多,本篇文章主要介绍的是单链表且无头结点。在严版数据结构(C语言 第2版)中,单链表采用的是有头节点,这两种形式,各有利弊。含头节点的单链表在学习时,可能会容易些,但是在实践中或者在力扣中做题时,很少会有带头节点。但是有时候做题,使用带头节点的单链表会简单许多,不常见。

概述

在这里插入图片描述

一个结点有两个部分组成,数据域(val),指针域(next)。

链表的实现

初始化

在无头单项非循环链表中,需要声明一个数据域和指针域,指针域指向的是下一个节点的地址,数据域是当前节点的数据。这里需要注意的是,在声明指针域是,因为是结构体指针类型,所以一定要写全了。

typedef int SLNDataType;

//初始化
typedef struct SListNode
{
	SLNDataType val;    //指针域
	struct SListNode* next; //指针域
}SLNode;

遍历单链表

遍历单链表,就是从单链表的第一个节点一直访问到单链表的最后一个节点。
形参接收到的是单链表的第一节点,然后需要用指针去维护节点。通过改变指针的指向,从而实现访问不同的节点。
当cue不为空指针时,继续访问下一个节点,操作为:cur=cur->next
当cur为空时,已经访问结束,无需继续遍历。
需要注意的是,这里的判断条件是cur为不为空,而不是cur->next为不为空,如果去判断cur->next,那么当cur->next==NULL时,访问的是最后一个节点为空,此时还没有遍历结束。

void SLTprintf(SLNode* phead)
{
	SLNode* cur = phead;
	while (cur)
	{
		printf("%d->", cur->val);
		cur = cur->next;
	}
	printf("NULL\n");
}

在这里插入图片描述

创建新节点

用malloc函数,在堆上申请一片空间来存储数据,当newcode为空时,表示开辟失败。

//创建新节点
SLNode* CreateNode(SLNDataType x)
{
	SLNode* newnode = (SLNode*)malloc(sizeof(SLNode));
	if (newnode == NULL)
	{
		perror("malloc fail");
		exit(-1);
	}

	newnode->val = x;
	newnode->next = NULL;
	return newnode;
}

尾插

尾插又称后插法,顾名思义,是将新节点逐个插入链表的尾部来创建链表。每次申请一个新节点,读入相应的数据元素值,同时需要一个尾指针指针链表的尾节点(tail)。
需要注意的是,当链表为空时,直接将新创建的节点当作第一个节点。尾插是封装在函数里实现的,对于函数来说,形参的改变不会改变形参,因此需要传递地址,即址传递,因此需要一个二级指针接收。
pphead是头指针(plist)的地址,*pphead是对它进行解引用。

void SLTPushBack(SLNode** pphead, SLNDataType x)
{
	SLNode* newnode = CreateNode(x);

	if (*pphead == NULL)
	{
		*pphead = newnode;
	}
	else
	{
		// 找尾
		SLNode* tail = *pphead;
		while (tail->next != NULL)
		{
			tail = tail->next;
		}

		tail->next = newnode;
	}
}

空链表和链表不存在的区别:

  • 空链表: 头指针为空,也就是plist==NULL表示是一个空链表
    在遍历、尾插、头插时允许空链表存在,在头删、尾删中不允许出现
  • 链表不存在: ppead==NULL,表示链表不存在,这种情况不允许存在的,因此,每次都需检查一下链表存不存在。

头插

头插法即前插法,逐个将新节点插入到链表的头部来创建,每次申请一个新节点,读入相应的数据元素值。传递的也是二级指针,将新节点的头节点给newnode->next,将newhead变成头节点。

//头插
void SLTPushFront(SLNode** pphead, SLNDataType x)
{
	SLNode* newnode = CreateNode(x);
	newnode->next = *pphead;
	*pphead = newnode;
}

尾删

尾删是将最后一个节点给释放掉,同时还需要保存倒数第二个节点,将它的指针域存放的地址改成NULL。需要两个指针,一个找尾,一个找倒数第二个节点,同时遍历。
空链表不能删,链表中只有一个节点的链表删除后会变成一个空链表,改变头指针需要存放地址,形参也是一个二级指针。

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

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

头删

头删需要改变的是头指针的地址,因此传的也是地址。tous需要考虑链表是否为空,如果是空链表就不能操作了,因此需要先断言。在删除头节点的时候,需要先保存一下头节点,否则释放了头节点,就找不到原来的头节点了。

//头删
void SLTPopFront(SLNode** pphead)
{
	assert(pphead);
	assert(*pphead);

	SLNode* tmp = *pphead;
	*pphead = (*pphead)->next;
	free(tmp);
}

单链表的查找

单链表的查找实际上就是遍历单链表,遍历过程中,找到你所需要的数值,如果是的,就返回当前节点,不是就继续往下遍历,直到链表为空。如果遍历完了还没有找到,那就说明你想查找的数值不在链表里面,返回空指针。

SLNode* SLTFind(SLNode* phead, SLNDataType x)
{
	SLNode* ptr = phead;
	while (ptr)
	{
		if (ptr->val == x)
		{
			return ptr;
		}
		else
		{
			ptr = ptr->next;
		}
	}
	return NULL;
}

在pos位置之前插入一个节点

遍历链表,找到pos之前的节点,然后按照尾插的方式插入即可。但是需要判断一种情况,如果pos就是第一个节点,那么在pos位置之前插入,那就相当于是头插了。有限制条件,pos一定是链表中的一个有效节点。

//在pos位置之前插入
SLNode* SLTInsert(SLNode** pphead, SLNode* pos, SLNDataType x)
{
	assert(pphead);
	assert(*pphead);
	assert(pos);

	//pos如果为头节点,就是头插
	if (*pphead == pos)
	{
		SLTPushFront(pphead, x);
	}
    //pos不是头节点
	else
	{
		SLNode* pre = *pphead;
		while (pre->next != pos)
		{
			pre = pre->next;
		}
		SLNode* newnode = CreateNode(x);
		pre->next = newnode;
		newnode->next = pos;
	}
}

在pos位置删除节点

pos需要是链表中存在的节点,链表不能为空。pos可能是头节点,因此需要二级指针,这种情况就相当于头删。删除pos节点时,需要一个指针保存pos前一个节点,让pos前一个结点的指针域直接指向pos下一个节点即可,释放pos,让pos=NULL。

//在pos位置删除节点
void SLTErase(SLNode** pphead, SLNode* pos)
{
	assert(pphead);
	assert(*pphead);
	assert(pos);

	if (*pphead == pos)
	{
		SLTPopFront(pphead);
	}
	else
	{
		SLNode* pre = *pphead;
		while (pre->next != pos)
		{
			pre=pre->next;
		}
		pre->next = pos->next;
		free(pos);
		pos = NULL;
	}
}

在pos位置后插入节点

需要保证pos是链表的一个有效节点,然后让新节点的指针域指向pos的下一个节点,然后让pos的指针域指向新节点。

在这里插入图片描述

//pos位置之后插入
void SLTInsertAfter(SLNode* pos, SLNDataType x)
{
	assert(pos);
	SLNode* newnode = CreateNode(x);

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

删除pos后一个节点

删除pos后一个节点,pos已经是一个节点,因此,链表不可能为空。
需要注意的是,这里不能直接是pos的指针域指向pos后的第二个节点,因为每一个节点都是通过malloc在堆上申请的,不使用的时候要主动的去释放掉,也就是free掉,把这块空间归还给操作系统,否则会导致内存泄漏。需要借助一个节点,保存待删节点,然后去释放借助的这一个节点并置为空指针即可。

//删除pos后一个节点
void SLTEraseAfter(SLNode* pos)
{
	assert(pos);
	assert(pos->next);

	SLNode* tmp = pos->next;
	pos->next = pos->next->next;

	free(tmp);
	tmp = NULL;
}

销毁

void SLTDestroy(SLNode** pphead){
	assert(pphead);

	SLNode* cur = *pphead;
	while (cur)
	{
		SLNode* next = cur->next;
		free(cur);
		cur = next;
	}

	*pphead = NULL;
}

结尾

单链表相对于顺序表会难很多,也会有很多的坑点,我们要去判断链表是否为空?(在遍历、尾插、头插时允许空链表存在),头节点是否存在?什么时候传二级指针?

在这里插入图片描述

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

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

相关文章

HDFS系统操作命令大全

一,前言 HDFS作为分布式存储的文件系统,有其对数据的路径表达方式 HDFS同linux系统一样,均是以/作为根目录的组织形式 linux:/usr/local/hello.txt HDFS:/usr/local/hello.txt 二,如何区分呢? L…

【数据结构】树与二叉树(三):二叉树的定义、特点、性质及相关证明

文章目录 5.1 树的基本概念5.1.1 树的定义5.1.2 森林的定义5.1.3 树的术语5.1.4 树的表示 5.2 二叉树5.2.1 二叉树1. 定义2. 特点3. 性质引理5.1:二叉树中层数为i的结点至多有 2 i 2^i 2i个,其中 i ≥ 0 i \geq 0 i≥0。引理5.2:高度为k的二叉…

yolov5--ptq--qat量化之敏感层分析

敏感层分析,应该是发生在ptq量化之前进行分析的操作,经过该操作,可得出哪些层不适合进行量化,则在接下来ptq时可以手动关闭这些层的量化。 进入敏感层分析函数sensitive_analysis中, 具体流程为: 首先验证…

打印机共享连接0x000003e3错误

重点重点:如使用下面教程还不行的朋友,请这样操作。 不管主机还是客户机,都运行“局域网共享一键修复工具”,并选择“简单共享(无密码)” 不管主机还是客户机,都操作一次此教程。 不管主机还是客…

Langchain-Chatchat项目:5.1-ChatGLM3-6B工具调用

在语义、数学、推理、代码、知识等不同角度的数据集上测评显示,ChatGLM3-6B-Base 具有在10B以下的基础模型中最强的性能。ChatGLM3-6B采用了全新设计的Prompt格式,除正常的多轮对话外。同时原生支持工具调用(Function Call)、代码…

如何用思维导图拆书

思维导图是一种非常有用的工具,可以被广泛应用于不同领域的人群。 阅读是我们获取知识和提升自己的重要途径,而做好读书笔记有助于加深对书中内容的理解和记忆。其中,使用思维导图作为读书笔记的工具,不仅能够帮助我们更好地整理…

lombok依赖介绍(帮助我们消除冗长代码,如get,set方法)

前言 lombok 是一个 Java 工具库,通过注解的方式,简化 Java 开发。要想使用 lombok 中的注解,我们需要先引入依赖,推荐看idea必装插件EditStarters(快速引入依赖),lombok是⼀款在编译期⽣成代码…

k8s提交spark应用消费kafka数据写入elasticsearch7

一、k8s集群环境 k8s 1.23版本,三个节点,容器运行时使用docker。 spark版本时3.3.3 k8s部署单节点的zookeeper、kafka、elasticsearch7 二、spark源码 https://download.csdn.net/download/TT1024167802/88509398 命令行提交方式 /opt/module/spark…

技术分享 | 被测项目需求你理解到位了么?

需求分析是开始测试工作的第一步,产品会先产出一个需求文档,然后会组织需求宣讲,在需求宣讲中分析需求中是否存在问题,然后宣讲结束后,通过需求文档分析测试点并且预估排期。所以对于需求的理解非常重要。 需求文档 …

壹[1],QT自定义控件创建(QtDesigner)

1,环境 Qt 5.14.2 VS2022 原因:厌烦了控件提升的繁琐设置,且看不到界面预览显示。 2,QT制作自定义控件 2.1,New/其他项目/Qt4 设计师自定义控件 2.2,设置项目名称 2.3,设置 2.4,设…

YOLOv7改进策略:一种新颖的可扩张残差(DWR)注意力模块,增强多尺度感受野特征,助力小目标检测

💡💡💡本文全网首发独家改进:一种新颖的可扩张残差(DWR)注意力模块,加强不同尺度特征提取能力,创新十足,独家首发适合科研 推荐指数:五星 DWR | 亲测在多个数据集能够实现涨点,多尺度特性在小目标检测表现也十分出色。 💡💡💡Yolov5/Yolov7魔术师,独…

【Java 进阶篇】Cookie 使用详解

欢迎阅读本篇博客,我们将深入研究 Java 中的 Cookie,从入门到精通,包括 Cookie 的基本概念、原理、使用方法以及一些高级技巧。无论你是新手还是有经验的开发者,希望这篇博客对你有所帮助。 第一部分:Cookie 是什么&a…

网络原理---封装和分用

文章目录 什么是封装和分用?封装应用层传输层网络层数据链路层物理层 分用物理层数据链路层网络层传输层应用层 什么是封装和分用? 我们前面讲过协议会分层,每一层都有各自的功能。而在数据传输的过程中,得按照顺序把每一层协议都…

数仓分层能减少重复计算,为啥能减少?如何减少?这篇文章包懂!

很多时候,看一些数据领域的文章,说到为什么做数据仓库、数据仓库要分层,我们经常会看到一些结论:因为有ABCD…等等理由,比如降低开发成本、减少重复计算等等好处 然后,多数人就记住了ABCD。但是&#xff0…

VScode连接Xshell 并解决【过程试图写入的管道不存在】报错

一.下载vscode 国内镜像: https://vscode.cdn.azure.cn/stable/6c3e3dba23e8fadc360aed75ce363ba185c49794/VSCodeUserSetup-x64-1.81.1.exe二.打开vscode在扩展搜索SSH并安装 三.添加主机 按F1选择添加新的ssh主机 按格式输入后在左边会出现电视的图标 之后输入…

十一、K8S之持久化存储

持久化存储 一、概念 在K8S中,数据持久化可以让容器在重新调度、重启或者迁移时保留其数据,并且确保数据的可靠性和持久性。 持久化存储通常用于程序的状态数据、数据库文件、日志文件等需要在容器生命周期之外的数据,它可以通过各种存储解…

项目管理之如何监控项目健康状态

项目管理是一个复杂且关键的过程,涉及到多个关键因素,包括项目名称、项目管理委员会成员、项目经理、项目生命周期的各个阶段以及资源泳道等。如何有效地监控项目的健康状态是确保项目成功的重要环节。本文将详细介绍项目管理全景图及其在风险识别中的应…

【差旅游记】公乌素遇到的那些司机师傅

哈喽,大家好,我是雷工! 出差人出差在外,城际间靠各种公共交通工具,但到了目的地的城镇,最后一公里往往少不了打车,或出租车,或摩的三轮车。 不同于公共交通,像飞机火车高…

【C++类和对象中:解锁面向对象编程的奇妙世界】

【本节目标】 1. 类的6个默认成员函数 2. 构造函数 3. 析构函数 4. 拷贝构造函数 5. 赋值运算符重载 6. const成员函数 7. 取地址及const取地址操作符重载 1.类的6个默认成员函数 如果一个类中什么成员都没有,简称为空类。 空类中真的什么都没有吗&#xf…

ConvNets 与 Vision Transformers:数学深入探讨

一、说明 我目睹了关于 Vision Transformer 的争论,讨论它们如何与 CNN 一样好或更好。我想知道我们是否也同样争论菠萝比西瓜好!或者马比海豚更好?其中许多讨论往往缺乏具体性,有时可能会歪曲上下文。 作为背景,在快速…