数据结构入门——03链表

news2024/12/29 9:10:57

1. 链表的结构

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

结点的data域存放数据元素ai,而next域是一个指针,指向ai的直接后继ai+1所在的结点。

1.1链表的分类

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

  • 单向

  • 双向

  • 不带头

  • 带头

  • 循环

  • 非循环

实际中常用的两种结构 

  • 无头单向循环链表

 无头单向循环链表结构简单,一般不会单独存储数据,更多的是作为其他数据结构的子结构。

  • 带头双向循环链表

带头双向循环链表最为复杂,可以单独存储数据,是实际使用中链表结构。虽然结构复杂,但是使用方便。

2. 无头单向循环链表的实现

2.0无头单向循环链表的表示

typedef int SLTDateType;
typedef struct SListNode
{
SLTDateType data;
struct SListNode* next;
}SListNode;

2.1 动态申请一个节点

// 动态申请一个节点
SListNode* BuySListNode(SLTDataType x)
{
	SListNode* newnode = (SListNode*)malloc(sizeof(SListNode));
	if (newnode == NULL)
	{
		printf("malloc fail\n");
		exit(-1);
	}
	else
	{
		newnode->data = x;
		newnode->next = NULL;
	}

	return newnode;
}

2.2 单链表打印

// 单链表打印
void SListPrint(SListNode* phead)
{
	SListNode* cur = phead;
	while (cur != NULL)
	{
		printf("%d->", cur->data);
		cur = cur->next;
	}
	printf("NULL\n");
}

2.3 单链表尾插

// 单链表尾插
void SListPushBack(SListNode** pphead, SLTDataType x)
{
	assert(pphead);

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

		tail->next = newnode;

		// 错误写法,这里没有链接起来
		/*SListNode* tail = *pphead;
		while (tail != NULL)
		{
		tail = tail->next;
		}

		tail = newnode;*/
	}
}

2.4 单链表的头插

// 单链表的头插
void SListPushFront(SListNode** pphead, SLTDataType x)
{
	assert(pphead);

	SListNode* newnode = BuySListNode(x);
	newnode->next = *pphead;
	*pphead = newnode;
}

2.5 单链表的尾删

// 单链表的尾删
void SListPopBack(SListNode** pphead)
{
	assert(pphead);

	// 也可以暴力检查为空的情况
	//assert(*pphead != NULL);

	// 1、空
	// 2、一个节点
	// 3、多个节点
	if (*pphead == NULL)  // 温柔检查
	{
		return;
	}
	else if ((*pphead)->next == NULL)
	{
		free(*pphead);
		*pphead = NULL;
	}
	else
	{
		/*SListNode* prev = NULL;
		SListNode* tail = *pphead;
		while (tail->next != NULL)
		{
		prev = tail;
		tail = tail->next;
		}

		free(tail);
		tail = NULL;
		prev->next = NULL;*/

		SListNode* tail = *pphead;
		while (tail->next->next != NULL)
		{
			tail = tail->next;
		}

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

2.6 单链表头删

// 单链表头删
void SListPopFront(SListNode** pphead)
{
	assert(pphead);

	// 1、空
	// 2、非空
	if (*pphead == NULL)
	{
		return;
	}
	else
	{
		SListNode* next = (*pphead)->next;
		free(*pphead);
		*pphead = next;
	}
}

2.7 单链表查找

// 单链表查找
SListNode* SListFind(SListNode* phead, SLTDataType x)
{
	SListNode* cur = phead;
	while (cur != NULL)
	{
		if (cur->data == x)
		{
			return cur;
		}

		cur = cur->next;
	}

	return NULL;
}

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

// 单链表在pos位置之前插入x
void SListInsert(SListNode** pphead, SListNode* pos, SLTDataType x)
{
	assert(pphead);
	assert(pos);

	// 1、pos是第一个节点
	// 2、pos不是第一个节点
	if (pos == *pphead)
	{
		SListPushFront(pphead, x);
	}
	else
	{
		SListNode* prev = *pphead;
		while (prev->next != pos)
		{
			prev = prev->next;
		}

		SListNode* newnode = BuySListNode(x);
		prev->next = newnode;
		newnode->next = pos;
	}
}


// 单链表在pos位置之后插入x
void SListInsertAfter(SListNode* pos, SLTDataType x)
{
	assert(pos);
	//SListNode* next = pos->next;
	//SListNode* newnode = BuySListNode(x);

	//pos->next = newnode;
	//newnode->next = next;

	SListNode* newnode = BuySListNode(x);
	newnode->next = pos->next;
	pos->next = newnode;
}

2.9 单链表删除pos位置的值

// 单链表删除pos位置的值
void SListErase(SListNode** pphead, SListNode* pos)
{
	assert(pphead);
	assert(pos);

	if (*pphead == pos)
	{
		SListPopFront(pphead);
	}
	else
	{
		SListNode* prev = *pphead;
		while (prev->next != pos)
		{
			prev = prev->next;
		}

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

// 单链表删除pos位置后的值
void SListEraseAfter(SListNode* pos)
{
	assert(pos);

	SListNode* next = pos->next;
	if (next)
	{
		pos->next = next->next;
		free(next);
		next = NULL;
	}
}

2.10销毁链表

void SListDestroy(SListNode** pphead)
{
	assert(pphead);

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

	*pphead = NULL;
}

3. 带头双向循环链表的实现

3.0 带头双向循环链表的表示

typedef int LTDataType;
typedef struct ListNode
{
	LTDataType data;
	struct ListNode* next;
	struct ListNode* prev;
}LTNode;

3.1双向链表初始化

//链表初始化
LTNode* ListInit()
{
	LTNode* phead = BuyLTNode(0);
	phead->next = phead;
	phead->prev = phead;

	return phead;
}

3.1创建新结点

// 创建返回链表的头结点.
LTNode* BuyLTNode(LTDataType x)
{
	LTNode* newnode = (LTNode*)malloc(sizeof(LTNode));
	if (newnode == NULL)
	{
		printf("malloc fail\n");
		exit(-1);
	}

	newnode->data = x;
	newnode->next = NULL;
	newnode->prev = NULL;
	return newnode;
}

3.2双向链表销毁

// 双向链表销毁
void ListDestory(LTNode* phead)
{
	assert(phead);
	LTNode* cur = phead->next;
	while (cur != phead)
	{
		LTNode* next = cur->next;
		//ListErase(cur);
		free(cur);
		cur = next;
	}

	free(phead);
	//phead = NULL;
}

3.3双向链表打印

// 双向链表打印
void ListPrint(LTNode* phead)
{
	assert(phead);

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

3.4双向链表尾插

// 双向链表尾插
void ListPushBack(LTNode* phead, LTDataType x)
{
	assert(phead);

	//LTNode* tail = phead->prev;
	//LTNode* newnode = BuyLTNode(x);

	//tail->next = newnode;
	//newnode->prev = tail;

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

	ListInsert(phead, x);
}

3.5双向链表尾删

// 双向链表尾删
void ListPopBack(LTNode* phead)
{
	assert(phead);
	// 链表为空
	assert(phead->next != phead);

	/*	LTNode* tail = phead->prev;
	LTNode* tailPrev = tail->prev;

	free(tail);
	tail = NULL;

	tailPrev->next = phead;
	phead->prev = tailPrev;*/

	ListErase(phead->prev);
}

3.6 双向链表头插

// 双向链表头插
void ListPushFront(LTNode* phead, LTDataType x)
{
	assert(phead);
	ListInsert(phead->next, x);
}

3.7 双向链表头删

// 双向链表头删
void ListPopFront(LTNode* phead)
{
	assert(phead);
	assert(phead->next != phead);

	ListErase(phead->next);
}

3.8双向链表查找

// 双向链表查找
LTNode* ListFind(LTNode* phead, LTDataType x)
{
	assert(phead);

	LTNode* cur = phead->next;
	while (cur != phead)
	{
		if (cur->data == x)
		{
			return cur;
		}

		cur = cur->next;
	}

	return NULL;
}

3.9双向链表在pos的前面进行插入

// 双向链表在pos的前面进行插入
void ListInsert(LTNode* pos, LTDataType x)
{
	assert(pos);
	/*LTNode* newnode = BuyLTNode(x);
	pos->prev->next = newnode;
	newnode->prev = pos->prev;

	pos->prev = newnode;
	newnode->next = pos;*/

	LTNode* newnode = BuyLTNode(x);
	LTNode* posPrev = pos->prev;

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

3.10双向链表删除pos位置的节点

// 双向链表删除pos位置的节点
void ListErase(LTNode* pos)
{
	assert(pos);

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

	free(pos);
	pos = NULL;

	prev->next = next;
	next->prev = prev;
}

4.顺序表和链表的优缺点

4.1顺序表

优点:
        空间利用率高、支持随机访问。
缺点:

  1. 中间或前面部分的插入删除时间复杂度O(N),要移动大量元素。
  2. 增容的代价比较大,需要连续内存空间,难以扩展。

 4.2链表
 

优点:

  1. 任意位置插入删除时间复杂度为O(1)。
  2. 没有增容消耗,按需申请节点空间,内存利用灵活,不用了直接释放。

 缺点:
        以节点为单位存储,不支持随机访问。

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

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

相关文章

利用matlab生成一注特别的大乐透机选程序

玩法说明: 代码: front randi([1,35],5,5) %前区随机生成(1-35),生成5行5列rear randi([1,12],1,2)%后区随机生成(1-12),生成1行2列front_r []%用来保存前区号码结果 rear_r rear%用来保存后区号码结果for i 1:size(front, 1) % 遍历每一行 for j 1:length(front(i,:)) …

OpenCV图像处理——直线拟合并找出拟合直线的起点与端点

引言 对轮廓进行分析,除了可以对轮廓进行椭圆或者圆的拟合之外,还可以对轮廓点集进行直线拟合。在 OpenCV 中,直线拟合通常是通过 cv::fitLine 函数实现的,该函数采用最小二乘法对一组 2D 或 3D 点进行直线拟合。对于 2D 点集&am…

【数据结构】详细剖析链表,带你实现单链表,双向链表,附链表编程练习题

目录 一. 链表 1. 链表的概念及结构 2. 单链表的实现 2.1 单链表节点结构 2.2 动态申请一个节点 2.3 单链表打印 2.4 单链表尾插 2.5 单链表头插 2.6 单链表尾删 2.7 单链表头删 2.8 单链表查找 2.9 单链表在pos后一位插入x 2.10 单链表删除pos后一位的值 2.11 …

《软件性能测试分析与调优实践之路》(第2版) 读书笔记(一)总体介绍(上)-真正从性能分析与调优来看性能测试

《软件性能测试分析与调优实践之路》(第2版) 是清华大学出版社出版的一本图书,作者为张永清,全书共分为9章,如下图所示 图书介绍:《软件性能测试分析与调优实践之路》(第2版) 1、为什么需要性能测试与分析 1)、了解…

成功交付西班牙足球俱乐部77英寸透明OLED模块订单

2024年8月初,我们完成了来自西班牙知名足球俱乐部的大宗订单交付。此次交付的10台77英寸透明OLED模块,标志着我们在高端显示技术领域迈出的又一重要步伐。这一订单不仅是我们目前单笔数量最多的77英寸模块订单,也是客户首次大规模采购我们产品…

自动化解决 reCAPTCHA v2:CapSolver 教程

对于那些经常进行网页爬取的人来说,你是否曾觉得 reCAPTCHA v2 就像是互联网版的过于严格的裁判员,总是在质疑你的真实性?但如果你能够轻松且合规地与这些裁判员达成和解,使你的网络搜索和自动化任务变得更顺畅,那该有…

【HarmonyOS NEXT】实现在当前Ability页面,拉起另一个Ability页面

【需求】 实现类似微信拉起支付页面。在手机应用程管理界面,可以看到同一个应用的两个窗口,如下图 【方案】 在EntryAbility的页面,点击按钮拉新的Ability 【步骤】 为EntryAbility准备页面 新建FirstAbilityPage页面将EntryAbility中的启动…

LivePortrait V3版:新增精确的肖像编辑,精准操控五官比如眉毛鼻子摇头眨眼撇嘴等,本地一键整合包下载

LivePortrait,这个名字听起来就像是魔法,但它其实是现实世界中的黑科技。想象一下,你那尘封已久的相册里,那些定格在时间里的笑脸,突然间动了起来,眨眼、微笑、甚至说话,这不再是电影里的场景&a…

企业源代码也需要加密!源代码加密软件推荐,2024十款软件排行榜

在科技飞速发展的2024年,企业的源代码作为核心资产,其安全性至关重要。为了防止源代码泄露带来的巨大损失,选择一款合适的源代码加密软件势在必行。下面为您呈现 2024 年十款优秀的源代码加密软件排行榜。 1. GitGuardian 实时监控&#xf…

从0到1:AI与低代码如何推动企业创新

引言 在当今瞬息万变的商业环境中,创新已成为企业在激烈市场竞争中立于不败之地的关键驱动力。面对快速变化的市场需求、技术进步和全球化竞争,企业亟需打破传统的增长模式,从而实现真正意义上的突破性创新。“从0到1”这一理念,源…

System V IPC奥秘:解锁共享内存、消息队列与信号量的高效通信之路

🍑个人主页:Jupiter. 🚀 所属专栏:Linux从入门到进阶 欢迎大家点赞收藏评论😊 目录 🍑system V共享内存 🍒共享内存的原理共享内存数据结构查看和删除共享内存资源的命令 🌻共享内存…

Spacedrive:一款基于VDFS的跨平台文件管理器深度解析

前言 你的文件不再被各种设备、云盘束缚,而是像魔法般汇聚在一个地方,触手可及,那将是怎样的畅快淋漓?Spacedrive,这个名字听起来就像是穿越时空的驱动器,它正悄悄改变着我们对文件管理的认知;…

开发物联网驱动拍卖软件平台:如何实现了服务质量的全面提升

在数字化转型的浪潮下,物联网(IoT)技术正深刻地改变着各行各业的运作模式,拍卖行业也不例外。通过物联网的集成应用,拍卖平台能够实现更高效、透明和个性化的服务,极大地提升用户体验和服务质量。本文将以“…

一起学习LeetCode热题100道(44/100)

44.二叉搜索树中第 K 小的元素(学习) 给定一个二叉搜索树的根节点 root ,和一个整数 k ,请你设计一个算法查找其中第 k 小的元素(从 1 开始计数)。 示例 1: 输入:root [3,1,4,null,2], k 1 输出&#…

u盘启动选择uefi还是legacy_u盘启动选择uefi还是legacy详细分析

最近有很多网友问我想用U盘安装系统,按照网上教程按快捷方式(一般是f12)出现选择U盘菜单时,我到底是选择legacy开头的U盘还是uefi开头的U盘,其实这个取决你要安装什么系统或是磁盘分区类型是gpt还是mbr,比如2016年后出来的笔记本和…

EasyRecovery17中文版永久汉化版电脑数据恢复工具下载

🎈🎉安利时间到!今天要跟大家分享的是——EasyRecovery17中文版的最新功能!🎉🎈 🌟✨ “数据恢复小能手” ✨🌟 让我来介绍一下这款软件的主打特点。 EasyRecovery17中文版是一款强…

谷歌账号活动异常,或者申诉回来以后需要手机验证的原因,以及验证手机号的错误操作和正确操作

有一些朋友在使用谷歌账号的时候,会遇到无法直接登录的情况,输入用户名、密码以后,提示说账号活动异常,需要验证手机号。 通常有以下两种情形和界面,出现这种情形的原因分别如下。 一、谷歌账号登录需要输入手机号码…

教你如何训练多模态理解模型

出发点:最近因为工作的需要,在研究多模态理解模型,看了最近一两年比较火的一些论文,感觉Nvidia的VILA工作可以作为比较好的多模态理解模型训练教程,也在这里介绍给大家。 多模态理解模型:也叫Large Vision…

玩游戏的时候怎么录屏?三种实用技巧

在数字化娱乐时代,录制游戏视频已成为玩家分享游戏体验的重要方式。录屏不仅能够记录精彩的游戏瞬间,还能用于制作教程、分析游戏策略或进行游戏直播。那么,玩游戏的时候怎么录屏呢?本文将介绍三种实用的录屏方法,帮助玩家们在畅…

WPF篇(20)- Menu菜单+ContextMenu上下文菜单+StatusBar状态栏

Menu菜单 Menu控件继承于MenuBase,而MenuBase继承于ItemsControl。所以学习Menu之前,要先了解一下MenuBase基类。它是一个抽象类,拥有一个ItemContainerTemplateSelector模板选择器,并重写了一些关于键盘和鼠标的方法。 Menu的子…