带头双向循环链表

news2024/11/17 15:14:53

目录

  • 1 链表的种类
    • 1.1 第一对
    • 1.2 第二对
    • 1.3 第三对
    • 1.4 常用
  • 2 带头双向循环链表的实现
    • 2.1 创建
    • 2.2 初始化
    • 2.3 打印
    • 2.4 尾插
    • 2.5 尾删
    • 2.6 头插
    • 2.7 头删
      • 2.7.1 两个变量
      • 2.7.2 三个变量
    • 2.8 查找x所在位置
    • 2.9 在pos节点前插入x
    • 2.10 删除pos节点
    • 2.11 销毁链表

1 链表的种类

1.1 第一对

1、单链表

ac4e44f4605942a38ea0b6db1bd2a06a.png

2、双向链表

dc3d756dc4574ff0bdcdebc320c0b2d0.png

1.2 第二对

3、不带头单链表

c64eba1827f6468ca7e0ed46bb0afd19.png

4、带头单链表

d7a8c754d8264635ad728c846423713d.png

1.3 第三对

5、单链表

4c9e0645fd174722bcbf236056b72593.png

6、循环单链表

e7a4a67b8c434ad59e02808e04d5eda7.png

1.4 常用

  • 无头单向非循环链表

3bdfa0b23a7845af809c998c22338072.png

​ 结构简单,一般不会单独用来存数据。实际中更多是作为其他数据结构的子结构,如哈希桶、图的邻接表等等。另外这种结构在笔试面试中出现很多。

  • 带头双向循环链表

07d5a66847dc47cab9e49053949bef77.png

​ 结构最复杂,一般用在单独存储数据。实际中使用的链表数据结构,都是带头双向循环链表。这个结构虽然结构复杂,但是使用代码实现以后会发现结构会带来很多优势,实现反而更简单。

2 带头双向循环链表的实现

2.1 创建

typedef int LTDateType;    // 定义存入的数据类型,以后方便修改

typedef struct LTNode
{
	LTDateType data;
	struct LTNode* next;   //下一个节点
	struct LTNode* prev;   //前一个节点
}LTNode;

2.2 初始化

思路

65ecee5e6415420782a8c0cb8bdd25b4.png

声明

LTNode*  ListInit();

定义

LTNode*  ListInit()
{
    LTNode* phead = (LTNode*)malloc(sizeof(LTNode)); // malloc一个哨兵位头节点
    phead->next = phead; // 使头尾相连
    phead->prev = phead;

    return phead;
}

2.3 打印

声明

void ListPrint(LTNode* phead);

定义

void ListPrint(LTNode* phead)
{
	assert(phead);

	LTNode* cur = phead->next;
	while (cur != phead) // cur不等于head时执行
	{
		printf("%d\n", cur->data);
		cur = cur->next;
	}
    printf("\n");
}

2.4 尾插

思路:

100c3b4b870045fab7486dbbc1d2efd5.png

声明

void ListPushBack(LTNode* phead, LTDateType x);

定义

void ListPushBack(LTNode* phead, LTDateType x)
{
	assert(phead);
    
    //1.
	LTNode* tail = phead->prev;
	LTNode* newnode = (LTNode*)malloc(sizeof(LTNode)); // 创建新节点
	newnode->data = x;    // 先存数据

	tail->next = newnode;    //使尾和新节点相连
	newnode->prev = tail; 

	phead->prev = newnode;   //使头和新节点相连
	newnode->next = phead;
    
    //2.直接调用Insert函数
	ListInsert(phead, x); 
}

注意

  • 由于是带有头节点,所有尾插时不改变plist,只改变plist里指向的结构体,所以不用传地址
  • 而无头单向非循环单链表要改变plist(NULL),所以要传地址
  • 返回值也不用传地址

2.5 尾删

思路

6ac07a1cd21447b2be2bc4ea5c352fb1.png

声明

void ListPopBack(LTNode* phead);

定义

void ListPopBack(LTNode* phead)
{
	assert(phead);

	LTNode* tail = phead->prev;
	phead->prev = tail->prev;
	tail->prev->next = phead;
}

2.6 头插

思路

img

定义

void ListPushFront(LTNode* phead, LTDateType x)
{
	assert(phead);
	// 1.常规方法
	/*LTNode* next = phead->next;
	LTNode* newnode = BuyListNode(x);

	phead->next = newnode;
	newnode->prev = phead;
	 
	newnode->next = next;
	next->prev = newnode;*/
	
	// 2.直接调用insert函数
	ListInsert(phead->next, x);
}

2.7 头删

2.7.1 两个变量

img

2.7.2 三个变量

img

定义

void ListPopFront(LTNode* phead)
{
	assert(phead);
	assert(phead->next != phead);

	// 1.
	//LTNode* next = phead->next;
	//phead->next = next->next;
	//next->next->prev = phead;
	//free(next);
	//2. 差别不大
	LTNode* next = phead->next;
	LTNode* nextNext = next->next;
	free(next);
	phead->next = nextNext;
	nextNext->prev = phead;
}

2.8 查找x所在位置

定义:和Print打印差不多

LTNode* ListFind(LTNode* phead, LTDateType x)
{
	assert(phead);
	LTNode* cur = phead;
	while (cur)
	{
		if (cur->data == x)
			return cur;
		else if (cur->next == phead)
			return NULL;
		cur = cur->next;
	}
}

2.9 在pos节点前插入x

思路

img

定义

void ListInsert(LTNode* pos, LTDateType x)// 在pos节点之前插入
{
	assert(pos);
	LTNode* newnode = BuyListNode(x);
	LTNode* prev = pos->prev;
	prev->next = newnode;
	newnode->prev = prev;

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

2.10 删除pos节点

思路

img

定义

void ListErase(LTNode* pos)// 删除pos节点
{
	assert(pos);
	LTNode* prev = pos->prev;
	LTNode* next = pos->next;
	free(pos);
	prev->next = next;
	next->prev = prev;
}

注意:有了insert函数后,可以修改头插尾插函数,使其直接调用insert函数即可,头插即在headnext处插入即可,尾插即在head处插入即可

2.11 销毁链表

思路

img

void ListDestroy(LTNode* phead)
{
	LTNode* cur = phead->next;
	while (cur != phead)
	{
		LTNode* next = cur->next;
		free(cur);
		cur = next;
	}
}

但是,由于传的是一级指针,最后无法把phead(即哨兵位头节点)给销毁,所以在结束函数后,我们使用函数的人在主函数可以写上plist=NULL,手动置空

img

因为整个链表我们每个函数接口都用的一级指针,所以最后这里销毁也用一级指针,所以会有无法销毁哨兵位的问题,但是为了保证 接口一致性,我们并不把它写为二级指针(其他都一级就别二级了)

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

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

相关文章

AS环境,版本问题,android开发布局知识

项目模式下有一个build.gradle,每个模块也有自己的build.gradle Android模式下有多个build.gradle,汇总在一起。(都会有标注是哪个模块下的) C:\Users\Administrator\AndroidStudioProjects 项目默认位置 Java web项目与android项目的区别…

队列(循环数组队列,用队列实现栈,用栈实现队列)

基础知识 队列(Queue):先进先出的数据结果,底层由双向链表实现 入队列:进行插入操作的一端称为队尾出队列:进行删除操作的一端称为对头 常用方法 boolean offer(E e) 入队 E(弹出元素的类型) poll() 出队 peek() 获取队头 int size 获取队列元素个数 boolean isEmpty(…

Spring Boot项目中热点场景详解(万字总结)

前言 「作者主页」:雪碧有白泡泡 「个人网站」:雪碧的个人网站 「推荐专栏」: ★java一站式服务 ★ ★ React从入门到精通★ ★前端炫酷代码分享 ★ ★ 从0到英雄,vue成神之路★ ★

循环读取图像实例

list image files (::mageDirectory,Extensions,Options:ImageFiles) par_join算子用于在调用程序中等待所有在单独的子线程中启动的程序或算子,方法是将par_start(启动的线程)添加到相应的程序行中( by adding the par_start qu…

CleanMyMac X4.14.1最新版本下载

CleanMyMac X是一个功能强大的Mac清理软件,它的设计理念是提供多个模块,包括垃圾清理、安全保护、速度优化、应用程序管理和文档管理粉碎等,以满足用户的不同需求。软件的界面简洁直观,让用户能够轻松进行日常的清理操作。 使用C…

(32)测距仪(声纳、激光雷达、深度摄影机)

文章目录 前言 32.1 单向测距仪 32.2 全向性近距离测距仪 32.3 基于视觉的传感器 前言 旋翼飞机/固定翼/无人车支持多种不同的测距仪,包括激光雷达(使用激光或红外线光束进行距离测量)、360 度激光雷达(可探测多个方向的障碍…

GEE土地分类——Property ‘B1‘ of feature ‘LE07_066018_20220603‘ is missing.错误

简介: 我正在尝试使用我在研究区域中选择的训练点对图像集合中的每个图像进行分类。就背景而言,我正在进行的项目正在研究陆地卫星生命周期内冰川面积的变化以及随后的植被变化。这意味着自 1984 年以来,我正在处理大量图像,每年一…

C语言qsort函数

排序qsort int int cmp(const void *a, const void *b) {return *(int *)a - *(int *)b;//先强转成int型,后解引用取值比较大小 }字符串数组 char a[] “hello world” //字符串数组,存放的是字符 int cmp(const void *a, const void *b) {return *(…

【linux进程(二)】如何创建子进程?--fork函数深度剖析

💓博主CSDN主页:杭电码农-NEO💓   ⏩专栏分类:Linux从入门到精通⏪   🚚代码仓库:NEO的学习日记🚚   🌹关注我🫵带你学更多操作系统知识   🔝🔝 进程状态管理 1. 前言2. 查看…

Nacos单机配置集群配置

大家好我是苏麟今天带来Nacos单机配置和集群配置 Nacos报错 理论上启动nacos,只需要双击startup.cmd文件(win下),就可以直接启动,但是从GitHub上下载当前最新版Nocas(下载地址),下载后进入bin目录双击sta…

生成Release版本的.pdb文件

软件分为Debug版本、Release版本这2种版本,其中Debug版本是带有.pdb调试信息文件,而Release版本不带.pdb调试信息文件。软件发布时,一般采用Release版本,若因内存泄漏、数组访问越界、除零错误、磁盘读写错误等异常,造…

PLL锁相环倍频原理

晶振8MHz,但是处理器输入可以达到72MHz,是因为PLL锁相环提供了72MHz。 锁相环由PD(鉴相器)、LP(滤波器)、VCO(压控振荡器)组成。 处理器获得的72MHz并非晶振提供,而是锁…

中国1km分辨率月最低温和最高温度数据集(1901-2020)

简介: 中国1km分辨率月最低温度数据集(1901-2020)是根据CRU发布的全球0.5气候数据集以及WorldClim发布的全球高分辨率气候数据集,通过Delta空间降尺度方案在中国地区降尺度生成的。使用了496个独立气象观测点数据进行验证&#x…

深度学习基础 2D卷积(1)

什么是2D卷积 2D参数量怎么计算 以pytorch为例子,2D卷积在设置的时候具有以下参数,具有输入通道的多少(这个决定了卷积核的通道数量),滤波器数量,这个是有多少个滤波器,越多提取的特征就越有用…

如何使用 LeiaPix 让照片动起来

在过去,想要让照片动起来,需要使用专业的软件和技巧。但是,随着科技的发展,现在只需使用一个简单的工具,就可以轻松地让照片动起来。 LeiaPix 是一个免费的在线工具,可以将静态照片转换为动画。该工具使用…

六、【常用工具组】

文章目录 移动工具组移动工具画板工具: 路径选择工具组抓手工具组 移动工具组 移动工具 在不同图层中选择时,先点击对应图层,然后按住control键,再使用鼠标拖拽即可移动图层 按CtrlT选中图层,然后右键即可进行置入、…

好工具分享:阿里云价格计算器_一键计算精准报价

阿里云服务器价格计算器,鼠标选择云服务器ECS实例规格、地域、系统盘、带宽及购买时长即可一键计算出精准报价,阿里云服务器网分享阿里云服务器价格计算器链接地址: 阿里云服务器价格计算器 先打开阿里云服务器ECS页面 aliyunfuwuqi.com/go…

阿里云服务器IP地址查询方法(公网IP和私网IP)

阿里云服务器IP地址在哪查看?在云服务器ECS管理控制台即可查看,阿里云服务器IP地址包括公网IP和私有IP地址,阿里云百科分享阿里云服务器IP地址查询方法: 目录 阿里云服务器IP地址查询 阿里云服务器IP地址查询 1、登录到阿里云服…

常见的软件脱壳思路

单步跟踪法 1.本方法采用OD载入。 2.跟踪F8,实现向下的跳。 3.遇到程序回跳按F4。 4.绿色线条表示跳转没实现,不用理会,红色线条表示跳转已经实现! 5.刚载入程序有一个CALL的,我们就F7跟进去,不然程序很容…