【链表之单链表】

news2024/11/20 15:31:14

前言:链表是什么?

链表的操作

1.单链表的结构

2.头文件的包含

3.动态申请一个节点

4.单链表打印

5.单链表尾插

6.单链表头插

7.单链表尾删

8.单链表头删

9.单链表查找

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

11.单链表在pos位置之前插入x

12. 单链表删除pos位置之后的节点

13. 单链表删除pos位置的节点

什么是链表

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

通俗地讲,就是一个结构体有两个成员,一个是存放数据的成员,一个是指针成员,通常的单链表是第一个节点的指针成员存着下一个节点的地址,下一个节点的指针成员又存下一个节点的地址,这样每个节点之间连接起来,就叫做链表。

本文主要讲的是链表中的一种重要的形式:单链表。
在这里插入图片描述
上图就是链表的逻辑图,head 是一个指针,存放链表第一个节点的地址,所以可以形象地称为头指针。
在真实的物理地址中,并不存在所谓的箭头,这些箭头是我们想象出来的,助于理解的东西。
在这里插入图片描述
假设地址都是随机的,那么上图才是真实的物理图。
每个节点的指针存储的都是下一个节点的地址,这样就像链条一样连接起来。

有了这些,我们就可以实现单链表的各种操作了。

1.单链表的结构

typedef int SLTDataType;

typedef struct SListNode
{
	SLTDataType data;
	struct SListNode* next;
}SLTNode;

就像上面单链表的结构所讲,每个节点都有一个data 和一个指针,用来保存下一个节点的地址。

2.单链表的头文件包含

#include<stdio.h>
#include<stdlib.h>
#include<assert.h>

3.单链表节点的创建

SLTNode* BuyListNode(SLTDataType x)
{
	SLTNode* newnode = (SLTNode*)malloc(sizeof(SLTNode));
	assert(newnode);
	newnode->data = x;
	newnode->next = NULL;
	return newnode;
	//创建节点
}

断言的目的是防止申请空间失败而出错。

4.单链表的打印


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

5.单链表尾插法

void SListPushBack(SLTNode** pphead, SLTDataType x)
{
	assert(pphead);//*pphead = plist,pphead = &plist,地址是不可能为空的,如果为空,就是传过来的参数传错了。

	SLTNode*newnode = BuyListNode(x);//创建节点
	//如果一开始链表为空,即没有节点
	if (*pphead == NULL)
	{
		*pphead = newnode;
	}
	else
	{
		//找到尾结点
		SLTNode* ptail = *pphead;
		while (ptail->next != NULL)
		{
			ptail = ptail->next;
		}
		//找到尾了,插入新节点
		if (newnode != NULL)
		{
			newnode->data = x;
			newnode->next = NULL;
			ptail->next = newnode;
			//就是让尾节点的next存newnode的地址
		}
	}
}

尾插需要分情况来讨论,当链表为空时如何插入。

6.单链表头插

void SListPushFront(SLTNode** pphead, SLTDataType x)
{
	assert(pphead);//*pphead = plist,pphead = &plist,地址是不可能为空的,如果为空,就是传过来的参数传错了。

	SLTNode* newnode = BuyListNode(x);//创建节点
	//头插法不需要分情况讨论
	newnode->next = *pphead;
	*pphead = newnode;
}

头插不需要分情况讨论,不管链表是否为空,都可以满足要求。

7.单链表的尾删

void SListPopBack(SLTNode** pphead)
{
	assert(pphead);//*pphead = plist,pphead = &plist,地址是不可能为空的,如果为空,就是传过来的参数传错了。

	assert(*pphead != NULL);//空链表
	//一个节点的情况
	if ((*pphead)->next == NULL)
	{
		free(*pphead);
		*pphead = NULL;
		return;
	}

	//法1:不需要临时变量
	//assert(*pphead);
	//SLTNode* ptail = *pphead;
	//while (ptail->next->next!=NULL)
	//{
	//	ptail = ptail->next;
	//}
	找到尾结点
	//free(ptail->next);
	//ptail->next =  NULL;

	//法2:需要临时变量
	SLTNode* ptail = *pphead;
	SLTNode* prev = ptail;
	while (ptail->next!=NULL)
	{
		prev = ptail;
		ptail = ptail->next;
	}
	free(ptail);
	ptail = NULL;
	prev->next = NULL;

}

在尾删中,有两种方法,可以使用临时变量,也可以不使用临时变量,空间复杂度为O(1)。

8.单链表头删

void SListPopFront(SLTNode** pphead) // 头删
{
	assert(pphead);//*pphead = plist,pphead = &plist,地址是不可能为空的,如果为空,就是传过来的参数传错了。

	assert(*pphead != NULL);//针对链表为空的情况

	//让指向头节点的指针指向下一个节点
	SLTNode* pnext = (*pphead)->next;
	free(*pphead);
	*pphead = pnext;
}

值得注意的是,单链表的头插和尾插都可以使用带哨兵位的头节点来进行头插和尾插,带哨兵位的头节点式的头插或尾插的好处是,当链表为空时,不需要分类判断。
但是带哨兵位的头节点的头插或尾插也有劣势,就是哨兵位节点需要申请,并且需要释放掉。

9.单链表查找

SLTNode* SListFind(SLTNode* phead, SLTDataType x)//查找节点
{

	assert(phead!=NULL);
	SLTNode* cur = phead;
	while (cur)
	{
		if (cur->data == x)
		{
			return cur;
		}
		else
		{
			cur = cur->next;
		}
	}
	//最后没找到
	return NULL;
}

10.在pos位置之后插入节点

void SListInsertAfter(SLTNode* pos, SLTDataType x)
{
	SLTNode* newnode = BuyListNode(x);
	assert(newnode);
	
	//注意二者位置不能更换,否则整条链表的数据就丢失了
	newnode->next = pos->next;
	pos->next = newnode;
}

11.在pos位置之前插入节点

//在pos的前面插入节点
void SListInsert(SLTNode** pphead, SLTNode* pos, SLTDataType x)
{
	assert(pphead);//*pphead = plist,pphead = &plist,地址是不可能为空的,如果为空,就是传过来的参数传错了。
	assert(pos);//地址不能为空
	//不管三七二十一,先创建一个节点
	SLTNode* newnode = BuyListNode(x);
	SLTNode* posPrev = *pphead;
	//判断头插的情况
	if (*pphead == pos)
	{
		newnode->next = pos;
		*pphead = newnode;
	}//或者调用头插接口
	else
	{
		while (posPrev->next != pos)
		{
			posPrev = posPrev->next;
		}
		posPrev->next = newnode;
		newnode->next = pos;
	}

}

可以比较发现,在pos位置之前和在pos位置之后插入节点的区别是,在pos位置之后插入节点的方法更简单,效率更高。

12.单链表删除pos位置之后的节点

void SlistEraseAfter(SLTNode* pos)//删除pos后一个节点
{
	assert(pos);//地址不能为空
	assert(pos->next!=NULL);
	SLTNode* Erasenode = pos->next;
	pos->next = Erasenode->next;
	free(Erasenode);
	Erasenode = NULL;
}

13.单链表删除pos位置的节点

void SlistErase(SLTNode** pphead, SLTNode* pos)//删除pos位置
{
	assert(pphead);//*pphead = plist,pphead = &plist,地址是不可能为空的,如果为空,就是传过来的参数传错了。

	assert(pos);//地址不能为空
	assert(*pphead != NULL);//空链表无法删除
	//链表只有一个节点的情况
	if ((*pphead)->next == NULL)
	{
		free(*pphead);
		*pphead = NULL;
		return;
	}
	//删除头节点的情况,相当于头删,可以调用头删接口
	if (*pphead == pos)
	{
		(*pphead) = pos->next;
		free(pos);
		pos = NULL;
		return;
	}

	if (pos->next == NULL)//尾删的情况
	{
		SLTNode* pnext = *pphead;
		while (pnext->next != pos)
		{
			pnext = pnext->next;
		}
		free(pos);
		//pos = NULL;//这里pos是形参,置空无意义
		pnext->next = NULL;
		return;
	}

	SLTNode* posprev = *pphead;
	while (posprev->next != pos)
	{
		posprev = posprev->next;
	}
	posprev->next = pos->next;
	free(pos);
	//pos = NULL;//这里pos是形参,置空无意义,
	//不过好习惯还是置空
}

对比删除pos位置和删除pos位置之后的节点的方法比较可知,删除pos位置的节点需要知道pos位置之前的节点,需要遍历链表,时间复杂度为O(n)。

删除pos位置之后的节点,不需要遍历链表,只需直接删除即可。
在参数部分,pos可以使用int类型,也可以使用结构体类型,个人认为使用结构体类型更全面,使用结构体类型可以使用更多功能。

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

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

相关文章

【数据挖掘】基于粒子群算法优化支持向量机PSO-SVM对葡萄酒数据集进行分类

1.粒子群算法的概念 PSO是粒子群优化算法&#xff08;Particle Swarm Optimization&#xff09;的英文缩写&#xff0c;是一种基于种群的随机优化技术&#xff0c;由Eberhart和Kennedy于1995年提出。粒子群算法是模仿昆虫、兽群、鸟群和鱼群等的群集行为&#xff0c;这些群体按…

中国电子学会2021年03月份青少年软件编程Scratch图形化等级考试试卷三级真题(含答案)

2021-03Scratch三级真题 分数&#xff1a;100题数&#xff1a;38 一、单选题(共25题&#xff0c;每题2分&#xff0c;共50分) 1.在《采矿》游戏中&#xff0c;当角色捡到黄金时财富值加1分&#xff0c;捡到钻石时财富值加2分&#xff0c;下面哪个程序实现这个功能&#xff1…

【软件测试】资深测试总结的测试必备8点,堪称测试人的好莱坞大片......

目录&#xff1a;导读前言一、Python编程入门到精通二、接口自动化项目实战三、Web自动化项目实战四、App自动化项目实战五、一线大厂简历六、测试开发DevOps体系七、常用自动化测试工具八、JMeter性能测试九、总结&#xff08;尾部小惊喜&#xff09;前言 测试8板斧。测试8板…

Task8:Excel的数据可视化

目录一 条形图二 条件单元格格式三 迷你图四 练习题一 条形图 【例子】直观的展示销售额之间的差别 方法&#xff1a;【开始】–>【条件格式】–>【数据条】 【只想显示条形图&#xff0c;不想显示金额】 1.条形图区域—>条件格式—>管理规则 2.选择设置的规则&a…

单应性Homography梳理,概念解释,传统方法,深度学习方法

Homography 这篇博客比较清晰准确的介绍了关于刚性变换&#xff0c;仿射变换&#xff0c;透视投影变换的理解 单应性变换 的 条件和表示 用 [无镜头畸变] 的相机从不同位置拍摄 [同一平面物体] 的图像之间存在单应性&#xff0c;可以用 [透视变换] 表示 。 opencv单应性变换求…

Active Directory计算机备份和恢复

在Active Directory&#xff08;AD&#xff09;环境中&#xff0c;用户通过域中的计算机认证他们自身。从AD中删除这些计算机账户时&#xff0c;系统也会自动从域中删除它们。于是&#xff0c;用户不能再通过些计算机登录网络。为允许用户访问域资源&#xff0c;必须恢复这些已…

聚集千百个企业管理系统的API资产,打造API资产全生命周期一站式集成体验

API——接口&#xff0c;作为软件世界中的连接服务和传输数据的重要管道&#xff0c;已经成为数字时代的新型基础设施&#xff0c;是各领域驱动数字变革的重要力量之一。传统企业集成主要采用点对点或ESB集成方式&#xff0c;基于全新API战略中台的API新型集成方式通过解耦系统…

SpringBoot跨域请求解决方案详细分析

跨域的定义 跨域是指不同域名之间的相互访问&#xff0c;这是由浏览器的同源策略决定的&#xff0c;是浏览器对JavaScript施加的安全措施&#xff0c;防止恶意文件破坏。同源策略&#xff1a;同源策略是一种约定&#xff0c;它是浏览器最核心的也是最基本的安全策略&#xff0…

【数据产品】缓存设计

背景&#xff1a;为什么需要做缓存&#xff1f; 我所做的产品的指标设计越来越复杂&#xff0c;查询性能也随之下降。因此需要增加缓存层&#xff0c; 以提高接口查询效率。 哪些层需要做缓存&#xff1f; 随着指标系统的应用&#xff0c;该产品的查询逻辑也越来越简单&…

二分查找核心思路--单调性--极值

在最初的二分查找中&#xff0c;我们将一组数据按大小排序&#xff0c;然后根据arr[mid]与要查找的k的大小比较&#xff0c;从而每次去掉一半的数字&#xff0c;使时间复杂度简化为O&#xff08;logN&#xff09;。 排序本质上是让数据的单调性统一&#xff0c;变为单增或单减…

spring中的JSR-303统一校验

1.在前后端的传输参数的过程中数据在何处校验? 在前后端都需要进行校验,只是分工不同. 2.各个层的校验内容: 1.Controller层主要负责校验残水的合法性,包括: 必填的参数字段,数据格式的校验 2.Service层的业务校验是审核业务中的规则的相关内容,比如:课程已经审核通过所以提…

vue3 为何比 vue2 快

vue3 为何比 vue2 快 测试环境&#xff1a;https://vue-next-template-explorer.netlify.app/ 1、proxy 响应式 vue3 优缺点&#xff1a; 深度监听性能更好可监听 新增 / 删除 属性可监听数组变化Proxy 能规避 Object.defineProxy 的问题Proxy 无法兼容所有浏览器&#xff…

OAuth2介绍

目录 一、什么是OAuth2 二、OAuth2中的角色 三、认证流程 四、令牌的特点 五、OAuth2授权方式 授权码 隐藏方式 密码方式 凭证方式 一、什么是OAuth2.0 概念&#xff1a;第三方授权解决方案 OAuth2.0是目前使用非常广泛的授权机制&#xff0c;用于授权第三方应用获取…

[NRF52] mesh DFU

mesh DFU升级过程&#xff1a;完整流程&#xff1a;以前nRF SDK DFU的实现是通过nRF51 Dongle配合主机nRF connect工具&#xff0c;且借助Secure DFU的后台式更新速率较快&#xff08;见另一篇笔记&#xff09;&#xff0c;现在的nRF mesh DFU分角色&#xff0c;全都由DK充当&a…

什么是单体应用?什么是微服务?

Monolith&#xff08;单体应用&#xff09;&#xff0c; 也称之为单体系统或者是 单体架构 。就是一种把系统中所有的功能、模块、组件等耦合在一个应用中应用最终打成一个(war,jar)包使用一个容器(Tomcat)进行部署&#xff0c;通常一个应用享用一个数据库。 也就是将所有的代码…

Java版数据结构与算法笔记

文章目录一、数据结构与算法概述及题目1、数据结构和算法的关系2、线性结构与非线性结构Ⅰ-线性结构Ⅱ-非线性结构3、经典面试题Ⅰ-字符串匹配问题&#xff1a;Ⅱ-汉诺塔游戏Ⅲ-八皇后问题:Ⅳ-马踏棋盘算法4、几个实际编程中遇到的问题Ⅰ-字符串替换问题Ⅱ-一个五子棋程序Ⅲ-约…

这家芯片企业,从创立之初就用 Authing 管理身份

在德州仪器和苹果的经验&#xff0c;让我深知统一身份管理要从 Day 1 做起。——Alpha Cen 联合创始人 & CEO 王璠 案例亮点&#xff1a; 打通 2000 主流应用、满足芯片初创企业统一身份需求 一周快速上线&#xff0c;产品开箱即用&#xff0c;后续无需费力运维 基于协同…

Pytorch安装及环境配置详细教程(CUDA版本)

文章目录前言一、查看GPU支持的CUDA版本二、安装CUDA三、确定torch、torchvision和python版本四、安装anaconda五、安装torch和torchvision前言 安装cuda版本的pytorch时踩了不少坑&#xff0c;网上安装pytorch的版本很多&#xff0c;一般的教程都是到pytorch的官网&#xff0…

相比传统专线网络,爱快、飞连等主流SD-WAN方案好在哪里?

当企业发展至一定的规模后&#xff0c;移动办公、异地办公会逐渐成为刚需&#xff0c;这就对企业的网络架构提出了新的挑战。 一般来说&#xff0c;企业在发展中所遇到的这些新增的网络需求&#xff0c;有两种比较常见的解决方案&#xff0c;其中之一是专线连接 。专线虽然简单…

【Unity3D插件】Build Report Tool插件,Build报告,优化包体,查看资源占用

推荐阅读 CSDN主页GitHub开源地址Unity3D插件分享简书地址我的个人博客 大家好&#xff0c;我是佛系工程师☆恬静的小魔龙☆&#xff0c;不定时更新Unity开发技巧&#xff0c;觉得有用记得一键三连哦。 一、前言 本篇文章介绍一下Build Report Tool插件的使用。 Build Repor…