数据结构之带头循环双向链表

news2024/9/22 15:26:48

目录

1.何为双链表?

2.带头循环双向链表

1.函数接口与结构体

2.初始化链表

3.销毁链表

4.打印链表

5.创建节点

6.尾插

7.尾删

8.头插

9.头删

10 查找节点

11.在pos前插入x

12.删除pos位置的值


在学习了单链表之后,我们发现单链表弥补了了顺序表的一些缺点,同时链表的结构体繁多,仔细计算链表的结构总共有8种,分为单链表与双链表,带头结点与不带头结点,循环与非循环。对于单链表,我们发现其自身也存在了一些问题

1.无法从后往前走

2.数据操作时间复杂度较高

3.无法找到结点的前驱

为了解决单链表的不足,我们引入的双链表的学习。

对于单向不带头不循环链表:结构简单,一般不会单独用来存放数据。实际中更多是用作其他数据结构的子结构,,如哈希桶,图的邻接表等等,另外这种结构在笔试面试中更多。

对于双向带头循环链表:结构最复杂,一般用于单独存放数据。实际中使用的链表数据结构都是带头双向循环链表。另外这个结构索然复杂,但是用代码实现起来会发现其结构有更大的优势,实现反而简单。

因为双向带头循环链表更适合我们实际中保存数据,所以我们今天就来学一下带头循环双向链表。

1.何为双链表?

顾名思义,在单链表的基础上又添加了一个指针,与单链表中的next指针效果一样,对于链表的物理模型不同的是,这里的指针与next是反向的。双链表完善了单链表的一些缺陷,在数据操作的时候也大大节省了时间。

2.带头循环双向链表

所谓带头循环双向链表就是在双链表的基础上添加了哨兵位,且尾节点的next->头节点,头结点的prev->指向尾节点,形成循环。此链表的结构看似复杂,但设计巧妙,我们在实现数据的一些操作功能时,巧妙的避免了许多麻烦,其次,不在考虑头节点为空的情况,省去了很多步骤。

1.函数接口与结构体

结构体的定义还是与双链表一样,其数据操作的函数接口也是一样的。

typedef int ST;
typedef struct ListNode
{
	struct ListNode* prev;
	ST data;
	struct ListNode* next;
}ListNode,*List;

ListNode* Listinit();//初始化

void Listdestroy(ListNode* phead);//销毁链表
void printlist(ListNode* phead);//打印链表
void pushback(ListNode* phead, int x);//尾插
void popback(ListNode* phead);//尾删
void pushfront(ListNode* phead, int x);//头插
void popfront(ListNode* phead);//头删
ListNode* findNode(ListNode* phead, ST x);//查找
void insertNode(ListNode* pos, ST x);//pos位置前插入
void EraseNode(ListNode* pos);//删除pos位置的值

2.初始化链表

因为我们需要的是带头循环双向链表,故初始化时,我们给定义的空链表初始化一个哨兵位,这里我们也不会用到哨兵位的数据,直接置为0.

ListNode*Listinit()
{
	//初始化给一个哨兵节点
     struct ListNode*phead = creatNode(0);
	  phead->next =phead;
	  phead->prev = phead;
	  return phead;
}

3.销毁链表

销毁链表时都要删除包括哨兵位,所以我们直接从哨兵位开始遍历:

void Listdestroy(ListNode* phead)
{
	assert(phead);
	while (phead)
	{
		ListNode* cur = phead->next;
		free(phead);
		phead = cur;
	}
}

4.打印链表

还是很简单,这里需要注意的是在遍历链表时,循环中,phead->next==phead就结束。

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

5.创建节点

该步骤只是为了后续操作方便。

ListNode* creatNode(ST x)//创建新节点
{
	ListNode* newnode = (ListNode*)malloc(sizeof(ListNode));
	if (newnode == NULL)
	{
		return NULL;
	}
	else
	{
     newnode->next = NULL;
	 newnode->prev = NULL;
	 newnode->data = x;
	 return newnode;
	}
	
}

6.尾插

 需要注意的一点是某个操作若与某些节点有关,我们可以先找到并保存这些节点,之后再惊醒操作,思路会更加清晰一些。

void pushback(ListNode* phead, int x)
{
	//先保存尾节点
	ListNode* tail = phead->prev;
	ListNode* newnode = creatNode(x);
	//链接
	tail->next = newnode;
	newnode->prev = tail;
	newnode->next = phead;
	
	phead->prev = newnode;
}

7.尾删

尾删需要注意的是链表传过来除了哨兵位,还有其它节点。

void popback(ListNode* phead)
{
	assert(phead);
	assert(phead->next);
	ListNode* oldtail = phead -> prev;
	ListNode* newtail = oldtail->prev;
	newtail->next = phead;
	phead->prev = newtail;
	free(oldtail);
	oldtail = NULL;
}

8.头插

void pushfront(ListNode* phead, int x)
{
	assert(phead);
	/*保存头节点地址*/
	ListNode* first = phead->next;
	ListNode* newnode = creatNode(x);
    newnode->next = first;
	first->prev = newnode;
	phead->next = newnode;
	newnode->prev = phead;
	
	//若没有保存头节点,在头插时应注意连接顺序。
	//先链接newnode与phead->next的节点
	
	/*ListNode* newnode = creatNode(x);
	phead->next->prev = newnode;
	newnode->next = phead->next;
	phead->next = newnode;
	newnode->prev = phead;*/
}

我们可以发现,根据连接顺序的不同,我们可以选择是否保存需要链接的哪些节点,否则就要注意链接的顺序,比如说在头插时,不保存头结点时,先与头节点链接,在与哨兵位节点链接,否则先与哨兵位链接,我们就会丢失头节点,找不到头结点。

9.头删

所谓头删原理与之前的一样,先保存哨兵位节点与头节点的下一个节点,之后链接哨兵位节点与头结点的下一个节点,之后再free掉头节点。

void popfront(ListNode* phead)
{
	assert(phead);
	if (phead->next==NULL)
	{
		printf("无元素可删\n");
		assert(phead);
	}
	ListNode* newhead = phead->next->next;
	ListNode* oldhead = phead->next;
	phead->next = newhead;
	newhead->prev = phead;
	free(oldhead);
	oldhead= NULL;
}

10 查找节点

 查找节点与打印链表的遍历方式相同,也很简单

ListNode* findNode(ListNode* phead, ST x)
{
	assert(phead);
	ListNode* cur = phead->next;
	while (phead != cur)
	{
		if (cur->data == x)
		{
			return cur;
		}
		cur = cur->next;
	}
	return NULL;
}

11.在pos前插入x

在这里,我们可以通过查找函数找到pos节点,之后在插入函数里传入pos节点。

插入的思路与之前的一样,先保存pos节点与pos前的节点,再链接新节点与pos,链接新节点与pos前的节点,比如插到d2前:

void insertNode(ListNode* pos, ST x)
{
	ListNode* newnode = creatNode(x);
	ListNode* front = pos->prev;
	ListNode* cur = pos;
	newnode->next = cur;
	cur->prev = newnode;
	front->next = newnode;
	newnode->prev = front;
}

在调用时:

int main()
{
	ListNode* p = NULL;
	p=Listinit();
	pushback(p, 1);
	pushback(p, 2);
	pushfront(p,4);
	ListNode* pos = findNode(p, 2);
	if (pos)
	{
		printf("找到了\n");
	}
	else
	{
		printf("未找到");
	}
	insertNode(pos, 3);
     return 0;
}

12.删除pos位置的值

还是一样,找到pos前后位置的两个节点,连接pos前后两个节点,在释放pos处的节点,完成删除。

void EraseNode(ListNode* pos)
{
	assert(pos);
	ListNode* tmp = pos;
	ListNode* cur = pos->next;
	ListNode* front = pos->prev;
	front->next = cur;
	cur->prev = front;
	free(tmp);
	tmp = NULL;
}

总结:带头循环双向链表,因为其独特的结构,使得我们在编译代码时省去了很多麻烦,在插入时不用判断头节点是否为空,在删除时,不用判断当只有一个节点时,是否是尾删。因为其循环的特性,判断也很方便/

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

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

相关文章

Spring 管理 Bean-IOC--基于注解配置 bean

目录 Spring 管理 Bean-IOC--基于注解配置 bean ● 基本介绍 ● 组件注解的形式有 代码演示--了解注解 UserDao UserService UserAction MyComponent 配置 beans.xml 注意 测试 注意事项和细节说明 自动装配 基本说明 应用实例需求 UserService UserAction 配置…

MLC LLM:将大模型运行在手机端的部署工具

前言 MLC LLM 是一个通用的解决方案 它允许任何语言模型在不同的硬件后端和本地应用程序集上进行本地部署 并为每个人提供一个高效的框架,以进一步优化模型的性能,满足他们自己的用例 其使命是让每个人都能在自己的设备(如手机端&#xf…

WordPress 不使用ftp更新

文章目录 摘要修改 wp-config.php文件处理 413 Request Entity Too Large修改nginx配置:client_max_body_size重启nginx 处理uploaded file exceeds the upload max filesize找到php.ini修改 upload_max_filesize重启php 摘要 每次 WordPress 有插件或主题更新都要…

计算机组成原理第五章(2)---中断

5.1概述 产生和应用 在IO设备和主机交换数据时,由于设备本身的机电特性的影响,其工作速度比较低,与CPU无法匹配,如果采用程序查询的方式需要CPU进行等待,但是如果在等待的过程中CPU可以执行其他的程序,可…

04_Uboot操作命令与其他命令

目录 BOOT 操作命令 bootz命令 bootm 命令 reset 命令 go 命令 run 命令 mtest 命令 BOOT 操作命令 uboot的本质工作是引导Linux,所以uboot肯定有相关的boot(引导)命令来启动Linux。常用的跟boot有关的命令有:bootz、bootm和boot。 bootz命令 要启动Linux,需要先将Lin…

《LearnUE——基础指南:上篇—1》——GamePlay架构之Actor和Component

目录 Component大法好,谁用谁知道!! 1.1.1 创世(UObject) 1.1.2 造物(Actor) 1.1.3 赋能(Component) Component大法好,谁用谁知道!&#xff0…

合肥职业技术学院分类考试招生职业技能考试 -- 计算机专业

考试大纲模块一 专业能力测试主要内容模块二 技术技能测试主要内容分值分布 分模块讲解模块一 专业能力测试计算机的发展、类型及其应用领域计算机技术的发展计算机应用领域 计算机中数据的表示、存储和处理计算机软、硬件系统的组成及主要技术指标计算机软、硬件系统的组成硬件…

YOLOv5-7.0训练中文标签的数据集

链接&#xff1a;https://pan.baidu.com/s/1KSROxTwyYnNoNxI5Tk13Dg 提取码&#xff1a;8888 以显示楷体为例&#xff08;上面的百度网盘里面有黑体、宋体、楷体ttf文件&#xff09; (1)将metric.py中&#xff1a; 将 sn.set(font_scale1.0 if nc < 50 else 0.8) # for …

iOS可视化动态绘制八种排序过程

一、可视化解决方案综述 1.交互UI综述 在本篇博客的第一部分我们先来整体的看一下我们Demo的功能。下方就是我们今天博客中的Demo的交互示意图。上方的输入框可以输入要排序元素的个数&#xff0c;下方输入的是300。程序会根据你输入的个数来随机生成数据&#xff0c;你输入30…

D. Edge Deletion(堆优化最短路)

Problem - D - Codeforces 给定一个由 n 个顶点和 m 条边组成的无向连通加权图。将从顶点 1 到顶点 i 的最短路径长度表示为 di。 你必须删除一些图中的边&#xff0c;使得最多只保留 k 条边。如果在删除边后&#xff0c;仍然存在从 1 到 i 的路径&#xff0c;其长度为 di&…

【数学建模】matlab的常用函数运用(1)

文章目录 1. matlab基本常识2. 常用输入输出函数2.1 输出函数2.2 拼接函数&#xff08;字符串的合并&#xff09;2.3 输入函数 3. 求和函数3.1 向量求和3.2 矩阵求和 4. 提取矩阵元素4.1 取第x行第y列的元素4.2 取指定行或列的所有元素4.3 取指定某些行的所有元素 1. matlab基本…

【OMNET++】V2X仿真

1.前言 车载无线通信技术V2X即Vehicle to Everything&#xff0c;是在车辆和任何会被该车辆所影响的实体之间分享信息的技术。V2X的主要动机是道路安全、交通效率和节能。 车辆影响实体的分类&#xff1a; Vehicle:对应其他车辆&#xff0c;对应通信V2V&#xff0c;对应设备是…

Linux学习[9]查找文件指令:which whereis locate find

文章目录 前言1. which2. whereis3. locate4. find总结&#xff1a; 前言 之前在弄交叉编译的时候需要找到gcc&#xff0c;gdb什么的在哪里&#xff1b;涉及到了查找文件指令。 这里对linux中的查找指令进行总结 1. which which指令一般用来寻找可执行文件的路径&#xff0c;…

C. Playing Piano(dfs)

Problem - C - Codeforces 小Paul想学弹钢琴。他已经有了一首想要开始演奏的旋律。为简单起见&#xff0c;他将这个旋律表示为键号序列a1,a2,…,an&#xff1a;数字越大&#xff0c;它就越靠近钢琴键盘的右侧。 Paul非常聪明&#xff0c;知道关键是正确地为他要演奏的音符分配…

git-windows安装

1.下载地址 https://www.git-scm.com/ 2.第一步&#xff0c;直接next 3.选择默认安装路径 4. 选择组件&#xff0c;默认 5.开始菜单是否创建&#xff0c;默认不创建 6.这里是设置 Git 默认编辑器&#xff0c;我们这里直接下一步 "Next" 7.调整新仓库中初始分支的名称…

观察 | 卫浴产业数字化转型下的中国智造样本

文 | 智能相对论 作者 | 佘凯文 数字技术的发展已成为全球科技变革向高端技术不断升级的方向。 年初&#xff0c;中共中央、国务院印发《数字中国建设整体布局规划》&#xff0c;这是党的二十大后党中央在我国数字化发展领域作出的最全面擘画&#xff0c;从顶层设计的高度对…

elasticsearch结构化查询(一)

在上一篇中我们介绍了DSL相关的知识&#xff0c;接下来我们将会学习elasticsearch的结构化查询&#xff0c;同时也实践一下上一篇的DSL的查询用法 什么是结构化搜索? 从《Elasticsearch权威指南》上摘取部分解释如下: 结构化搜索是指查询包含内部结构的数据。日期&#xff0…

CentOS 7.6更改yum源

使用字符串替换 我这里的操作参考了https://baijiahao.baidu.com/s?id1708418392526536542&wfrspider&forpc这篇文章&#xff0c;https://mirrors.tuna.tsinghua.edu.cn/help/centos/是清华大学官网教程。 /etc/yum.repos.d/CentOS-Base.repo文件如下&#xff1a; #…

Python的类与对象、构造方法、类与对象三大特性封装、继承和多态、类型注解

类与对象 1.Python的对象 使用对象组织数据 在程序中是可以做到和生活中那样&#xff0c;设计表格、生产表格、填写表格的组织形式的。 在程序中设计表格&#xff0c;我们称之为&#xff1a;设计类(class) class Student: name None #记录学生姓名 在程序中打印生产表格&…

【MySQL】函数和约束

如标题所说,本文重点只有两个:MySQL语句里面的函数和约束 目录 1. 函数1.1 字符串函数1.2 数值函数1.3 日期函数1.4 流程函数 2.约束2.1 外键的删除更新行为 1. 函数 因为在前一篇文章里面有讲到聚合函数,所以在这里就不重复介绍了,本文所介绍的函数有4类:字符串函数,数值函数…