【数据结构】-- 单链表 vs 双向链表

news2024/11/24 13:52:40

🌈 个人主页:白子寰
🔥 分类专栏:python从入门到精通,魔法指针,进阶C++,C语言,C语言题集,C语言实现游戏👈 希望得到您的订阅和支持~
💡 坚持创作博文(平均质量分82+),分享更多关于深度学习、C/C++,python领域的优质内容!(希望得到您的关注~) 

目录

 单链表和双向链表的比较

链表的打印

单链表

双向链表 

初始化

双向链表

开辟空间,写入新数据

单链表

双向链表

尾部插入数据

单链表

双向链表

头部插入数据

单链表 

双向链表

尾部删除数据

单链表

双向链表

头部删除数据

单链表

双向链表

查找

单链表 

双向链表 

在指定位置之前插入数据 

单链表

在指定位置之后插入数据

单链表

双向链表 

 删除指定位置数据

单链表

双向链表

删除指定位置后的数据

单链表

销毁链表

单链表

双向链表


 单链表和双向链表的比较

单向链表双向链表
指代不同链表的链接方向是单向的,访问链表时时要顺序读取从头开始访问每个数据的节点都有两个指针,即直接前驱和直接后驱
优点不同单个节点创建方便,普通的线性内存通常在创建的时候就需要设定数据的大小,节点的访问方便,可以通过循环/递归的方法访问到任意数据从双向链表中的任意一个节点开始,方便访问前驱节点和后继节点
缺点不同增加/删除节点复杂,需要多分配一个指针存储空间平均访问效率低于线性表,只能从头到尾遍历,只能找到后继,无法找到前驱(只能前进)

链表的打印

单链表

//链表的打印
void SLTPrint(SLTNode* phead)
{
	SLTNode* pcur = phead;
	while (pcur)
	{
		printf("%d->", pcur->data);
		pcur = pcur->next;
	}
	printf("NULL\n");
}

双向链表 

//打印
void LTPrint(LTNode* phead)
{
	assert(phead);
	LTNode* pcur = phead->next;
	while (pcur != phead)
	{
		printf("%d->", pcur->dada);
		pcur = pcur->next;
	}
	printf("\n");
}

初始化

双向链表

//双向链表初始化
LTNode* LTInit()
{
	//哨兵位设置为-1
	LTNode* phead = SLTBuyNode(-1);
	return phead;
}

开辟空间,写入新数据

单链表

//开辟空间,写入新数据
SLTNode* SLTbuyNode(SLTDatatype x)
{
	SLTNode* newnode = (SLTNode*)malloc(sizeof(SLTNode));
	if (newnode == NULL)
	{
		perror("malloc");
		exit(1);
	}

	//先插入,后数据为空(后为空)
	newnode->data = x;
	newnode->next = NULL;

	return newnode;//返回数据
}

双向链表

//扩容,申请新节点
LTNode* SLTBuyNode(LTDataType x)
{
	LTNode* newnode = (LTNode*)malloc(sizeof(LTNode));//扩容
	if (newnode == NULL)
	{
		perror("malloc");
		exit(1);
	}
	//数据赋值
	newnode->dada = x;
	//指向自己
	newnode->next = newnode->prev = newnode;

	return newnode;
}

尾部插入数据

单链表

//尾插
void SLTPushBack(SLTNode** pphead, SLTDatatype x)
{
	//传的参数不为空
	assert(pphead);

	SLTNode*newnode = SLTbuyNode(x);
	//链表为空,新节点作为phead
	//*pphead是指向第一个节点的指针
	if (*pphead == NULL)//头节点为空
	{
		*pphead = newnode;
		return;
	}

	//链表不为空,找尾节点
	//ptail作为尾节点
	SLTNode* ptail = *pphead;
	//先方向,后数据
	while (ptail->next)
	{
		ptail = ptail->next;
	}
	ptail->next = newnode;
}

双向链表

//尾插
void LTPushBack(LTNode* phead, LTDataType x)
{
	assert(phead);
	LTNode* newnode = SLTBuyNode(x);
	
	newnode->next = phead;
	newnode->prev = phead->prev;

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

头部插入数据

单链表 

//头插
void SLTPushFront(SLTNode** pphead, SLTDatatype x)
{
	//传参有效
	assert(pphead);
	//开辟空间,新数据进入
	SLTNode* newnode = SLTbuyNode(x);

	//先方向,后数据
	newnode->next = *pphead;
	*pphead = newnode;
}

双向链表

//头插
void LTPushFront(LTNode* phead, LTDataType x)
{
	assert(phead);
	LTNode* newnode = SLTBuyNode(x);

	newnode->next = phead->next;
	newnode->prev = phead;

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

尾部删除数据

单链表

//尾删
void SLTPopBack(SLTNode** pphead)
{
	//保证传参数据有效
	assert(pphead);
	//链表不能为空
	assert(*pphead);

	//链表不为空
	//链表只有一个节点
	if ((*pphead)->next==NULL)
	{
		free(*pphead);
		*pphead = NULL;
		return;
	}

	//有多个节点
	SLTNode* prev = NULL;
	SLTNode* ptail = *pphead;
	while (prev->next)
	{
		prev = ptail;
		ptail = ptail->next;
	}
	prev = NULL;

	//销毁尾节点
	free(ptail);
	ptail = NULL;
}

双向链表

//尾删
void LTPopBack(LTNode* phead)
{
	assert(phead);

	LTNode* del = phead->prev;

	del->prev->next = phead;
	phead->prev = del->prev;

	free(del);
	del = NULL;
}

头部删除数据

单链表

//头删
void SLTPopFront(SLTNode** pphead)
{
	//保证传参有效
	assert(pphead);
	//保证链表有效
	assert(*pphead);

	//让第二个节点成为新的头
	SLTNode* next = (*pphead)->next;
	//把旧的头节点释放掉
	free(*pphead);

	*pphead = next;//第一个节点 = 原来第二个节点
}

双向链表

//头删
void LTPopFront(LTNode* phead)
{
	assert(phead);

	LTNode* del = phead->next;

	phead->next = del->next;
	del->next->prev = phead;

	free(del);
	del = NULL;
}

查找

单链表 

SLTNode* SLTFind(SLTNode** pphead, SLTDatatype x)
{
	//保证传参有效
	assert(pphead);

	//遍历链表
	SLTNode* pcur = *pphead;
	while (pcur)
	{
		if (pcur->data == x)
		{
			return pcur;
		}
		pcur = pcur->next;
	}
	//没有找到
	return NULL;
}

双向链表 

//查找
LTNode* LTFind(LTNode* phead, LTDataType x)
{
	assert(phead);

	LTNode* pcur = phead->next;

	while (pcur != phead)
	{
		if (pcur->dada == x)
		{
			return pcur;
		}
		pcur = pcur->next;
	}
	return NULL;
}

在指定位置之前插入数据 

单链表

//在指定位置之前插入数据
void SLTInsert(SLTNode** pphead, SLTNode* pos, SLTDatatype x)
{
	//保证传参有效
	assert(pphead);
	//保证指定位置数据有效
	assert(pos);
	//链表也不能为空
	assert(*pphead);

	//新节点,开辟新空间
	SLTNode* newnode = SLTbuyNode(x);
	//pos刚好是头节点
	if (newnode->next = *pphead)
	{
		//头插
		SLTPushFront(pphead, x);
		return;
	}

	//pos不是头节点的情况
	SLTNode* prev = *pphead;
	if (prev->next != pos)
	{
		prev = prev->next;
	}
	prev->next = newnode;
	newnode->next = pos;
}

在指定位置之后插入数据

单链表

//在指定位置之后插入数据
void SLTInsertAfter(SLTNode* pos, SLTDatatype x)
{
	//保证目标数据有效
	assert(pos);

	//创建新节点
	SLTNode* newnode = SLTbuyNode(x);

	//先方向,后数据
	newnode->next = pos->next;
	pos->next = newnode;
}

双向链表 

//在pos位置之后插入数据
void LTInsert(LTNode* pos, LTDataType x)
{
	assert(pos);

	LTNode* newnode = SLTBuyNode(x);
	newnode->prev = pos;
	newnode->next = pos->next;

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

 删除指定位置数据

单链表

//删除pos节点
void SLTErase(SLTNode** pphead, SLTNode* pos)
{
	//保证传的参数有效
	assert(pphead);
	//保证单链表有效
	assert(*pphead);
	//保证目标数据有效
	assert(pos);

	//pos刚好是头节点,没有前驱节点
	if (*pphead == pos)
	{
		//头删
		SLTPopFront(pphead);
		return;
	}

	//pos不是头节点
	SLTNode* prev = *pphead;
	while (prev->next != pos)
	{
		prev = prev->next;
	}

	//释放
	prev->next = pos->next;
	free(pos);
	pos = NULL;
}

双向链表

//删除pos位置的数据
void LTErase(LTNode* pos)
{
	assert(pos);

	LTNode* del = pos;
	pos->prev->next = pos->next;
	pos->next->prev = pos->prev;

	free(del);
	del = NULL;
}

删除指定位置后的数据

单链表

//删除pos之后的节点
void SLTEraseAfter(SLTNode* pos)
{
	//保证目标数据有效
	assert(pos);
	//pos->next数据有效
	assert(pos->next);

	SLTNode* del = pos->next;
	pos->next = pos->next->next;

	//释放
	free(del);
	del = NULL;
}

销毁链表

单链表

//销毁链表
void SListDesTroy(SLTNode** pphead)
{
	//保证传的参数有效
	assert(pphead);
	//保证链表有效
	assert(*pphead);

	SLTNode* pcur = *pphead;
	while (pcur)
	{
		SLTNode* next = pcur->next;
		free(pcur);
		pcur = next;
	}
	*pphead = NULL;
}

双向链表

//销毁
void LTDesTroy(LTNode* phead)
{
	//哨兵位不能为空
	assert(phead);

	LTNode* pcur = phead->next;
	while(pcur != phead)
	{
		LTNode* next = pcur->next;
		free(pcur);
		pcur = next;
	}
	free(phead);
	phead = NULL;
}

***********************************************************分割线*****************************************************************************
完结!!!
感谢浏览和阅读。

等等等等一下,分享最近喜欢的一句话:

“成为正确答案的第一步,相信自己会是正确答案”。

我是白子寰,如果你喜欢我的作品,不妨你留个点赞+关注让我知道你曾来过。
你的点赞和关注是我持续写作的动力!!! 
好了划走吧。

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

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

相关文章

珠海华发实业股份有限公司副总毛冰清莅临天府锋巢直播产业基地考察调研

3月19日,珠海华发实业股份有限公司副总毛冰清拜访天府锋巢直播产业基地(以下简称天府锋巢),由产业招商总负责人姜国东进行接待。 基地建设情况 姜国东负责人介绍到,天府锋巢是由德商产投携手无锋科技于兴隆湖落地的成都…

从误差分解看Few-shot的核心问题

FSL训练过程一般都是最小化经验误差ERM。 同时,由于现实任务的实际数据分布 是未知的,因此无法找到一个最优的参数组合 ,能最小化期望损失(最小值多少也是未知的),我们能做的实际上是尽可能的去找一个参数…

基于SpringBoot + Vue实现的在线答疑系统设计与实现+毕业论文+答辩PPT

介绍 学生角色: 1.注册、登录功能:学生可以通过系统完成注册和登录操作,进入学生专属界面。 2.个人信息修改功能:学生可以查看和修改自己的个人信息,包括姓名、联系方式等。 3.问题发布功能:学生可以在线发…

功能测试如何到自动化测试,看这篇就够了。

🍅 视频学习:文末有免费的配套视频可观看 🍅 关注公众号:互联网杂货铺,回复1 ,免费获取软件测试全套资料,资料在手,薪资嘎嘎涨 本帖不仅给大家介绍自动化测试,更会提供大…

MySQL-10. 存储引擎、视图、mysql管理

10.1 存储引擎 存储引擎说白了就是如何存储数据、如何为存储的数据建立索引和如何更新、查询数据等技术的实现方法。因为在关系数据库中数据的存储是以表的形式存储的,所以存储引擎也可以称为表类型(即存储和操作此表的类型)。 存储引擎(Stor…

使用MobaXterm/winTerm之类的工具连接虚拟机时报错Permission denied或者Access denied的解决办法

一、虚拟机设置: 1.将点击【编辑】---> 【虚拟网络编辑器】 2.添加VMnet6 1)点击【DHCP设置】--->添加网络--->选中【仅主机模式】【将主机虚拟适配器连接到此网络】 2)子网ip写“192.168.66.0” 3)点击右下角【应用…

前端实现打开新标签页后,再次定位到该标签页

需求 A 页面中点击按钮可以打开新的标签页 B 并且向 B 页面发送消息数据。 当新的标签页 B 未关闭且符合同源策略时&#xff0c;再次点击按钮&#xff0c;可以自动跳转到标签页 B 并且发生消息数据。 B.html <script>window.onmessage evt > {console.log(evt.d…

xss跨站脚本攻击笔记

1 XSS跨站脚本攻击 1.1 xss跨站脚本攻击介绍 跨站脚本攻击英文全称为(Cross site Script)缩写为CSS&#xff0c;但是为了和层叠样式表(CascadingStyle Sheet)CSS区分开来&#xff0c;所以在安全领域跨站脚本攻击叫做XSS 1.2 xss跨战脚本攻击分类 第一种类型:反射型XSS 反射…

14.java openCV4.x 入门-Core之图像融合

专栏简介 &#x1f492;个人主页 &#x1f4f0;专栏目录 点击上方查看更多内容 &#x1f4d6;心灵鸡汤&#x1f4d6;我们唯一拥有的就是今天&#xff0c;唯一能把握的也是今天建议把本文当作笔记来看&#xff0c;据说专栏目录里面有相应视频&#x1f92b; &#x1f9ed;文…

每日一题(leetcode2529):正整数和负整数的最大计数--二分法

因为需要O&#xff08;logn&#xff09;的复杂度&#xff0c;所以考虑使用二分法&#xff0c;先找到负数里面的最大下标&#xff08;初始值定为-1&#xff09;&#xff0c;再找到第一个正数的下标&#xff08;初始值定为数组长度值&#xff09;。最后求出个数并进行比较即可。 …

说说你对算法中时间复杂度,空间复杂度的理解?如何计算?

一、前言 算法&#xff08;Algorithm&#xff09;是指用来操作数据、解决程序问题的一组方法。对于同一个问题&#xff0c;使用不同的算法&#xff0c;也许最终得到的结果是一样的&#xff0c;但在过程中消耗的资源和时间却会有很大的区别 衡量不同算法之间的优劣主要是通过时…

深度学习图像处理04:图像分类模型训练实战——动物分类

这篇博文不涉及理论知识&#xff0c;主要通过一个完整的深度学习模型训练流程&#xff0c;直观地了解深度学习图像分类任务。有关理论的部分&#xff0c;之前几篇博文已经涉及基础部分&#xff0c;之后也会对一些理论进行补充。 本文将结合代码&#xff0c;主要介绍三部分内容…

嵌入式应会的模电数电基础

AC/DC交直流 电压 欧姆定律 常见元器件 电阻器 并联电阻&#xff0c;增加通路&#xff0c;电阻更小&#xff0c;电流更大 串联电阻&#xff0c;电阻更大&#xff0c;电流越小 相同阻值的电阻&#xff0c;个头大小不同主要区别在功率容量、耐压能力和散热性能方面。 功率容量…

产品开发流程

产品开发流程 时间&#xff1a;2024年04月10日 作者&#xff1a;小蒋聊技术 邮箱&#xff1a;wei_wei10163.com 微信&#xff1a;wei_wei10 产品开发流程_小蒋聊技术_免费在线阅读收听下载 - 喜马拉雅欢迎收听小蒋聊技术的类最新章节声音“产品开发流程”。时间&#xff1a;…

MQ死信队列:面试题

所谓的死信队列只不过是我们自己定义的一个队列&#xff0c;注意对于这个队列只能人工干预 面试题&#xff1a;你们是如何保证消息不会丢失的 1&#xff0c;什么是死信 在RabitMQ中充当主角的就是消息&#xff0c;在不同场景下&#xff0c;消息会有不同地表现。 死信就是在…

Hugo 配置个人博客

下载 Hugo 下载地址: https://www.gohugo.org/ 或者点击百度云链接下载安装包: 链接&#xff1a;https://pan.baidu.com/s/1s2GuNuUKDjEXRzXgCvEYHQ?pwd6666 提取码&#xff1a;6666 安装 Hugo 在文件夹内建立新的文件夹, 在再内部建立两个文件夹, 分别命名为 bin 和 site.…

设计模式——2_8 策略(Strategy)

文章目录 定义图纸一个例子&#xff1a;如何切换坦克的攻击方式GameElement&#xff08;游戏元素&#xff09;TankFactory&#xff08;坦克工厂&#xff09;Tank&#xff08;坦克&#xff09; 医疗车和飞行车策略模式Behavior(行为)TankTankFactory 碎碎念策略和状态为什么我们…

深入浅出 -- 系统架构之微服务中OpenFeign最佳实践

前面我们讲了一下 Ribbon 和 RestTemplate 实现服务端通信的方法&#xff0c;Ribbon 提供了客户端负载均衡&#xff0c;而 RestTemplate 则对 http 进行封装&#xff0c;简化了发送请求的流程&#xff0c;两者互相配合&#xff0c;构建了服务间的高可用通信。 但在使用后也会发…

打开Visual Studio后出现Visual Assist报错弹窗

安装了新的VA插件后发现无论如何清理打开VS都会报这个旧版VA报错弹窗&#xff0c;修复VS、重装VA都解决不了 后来进到VS安装目录&#xff0c;删掉一个可疑文件后弹窗再也不出现了

设计模式之迭代器模式(上)

迭代器模式 1&#xff09;概述 1.概念 存储多个成员对象&#xff08;元素&#xff09;的类叫聚合类(Aggregate Classes)&#xff0c;对应的对象称为聚合对象。 聚合对象有两个职责&#xff0c;一是存储数据&#xff0c;二是遍历数据。 2.概述 迭代器模式(Iterator Patter…