带头结点的双向循环链表

news2025/1/10 2:52:22

目录

带头结点的双向循环链表

 1.存储定义

2.结点的创建

3.结点的初始化

4.尾插结点

5.尾删结点

6.头插结点

7.头删结点 

8.查找并返回结点

9.在pos结点前插入结点

10.删除pos结点

11.打印链表

 12.销毁链表

13.头插结点2.0版

 14.尾插结点2.0版


前言:

当我们使用单链表时,想要去找到前面的前驱结点的时候,我们发现了受到限制,因为当时的结点只能去找后面的结点。

于是,就到了这里的带头结点的双向循环链表

如图:

首先给出  双向链表的定义:是在单链表的每个结点中,再设置一个指向其前驱结点的指针域。

那双向循环链表就是最后一个结点的next指向头结点,头结点的前驱指向尾结点。

带头结点的双向循环链表

 1.存储定义

typedef int CLDataType;

typedef  struct ListNode
{
	CLDataType val;
	struct ListNode* next; //存放后面结点的指针
	struct ListNode* pre;  //存放前面结点的指针
}ListNode;

2.结点的创建

结点的创建,刚开始创建头结点我们一般会采用二级指针的方式,这里我们采用使用函数返回值的形式来避免野指针问题。

//创建链表
ListNode* CreateNode(CLDataType x) 
{
	//这里采用返回值的形式进行创建结点
	ListNode* newnode = (ListNode*)malloc(sizeof(ListNode));
	if (newnode == NULL) 
	{
		perror("malloc");
		exit(-1);
	}
	newnode->val = x;
	newnode->pre = NULL;
	newnode->next = NULL;
	return newnode;
}

 如果是刚开始创建的头节点的话:

3.结点的初始化

这个时候就能体现带头结点双向循环链表的好处了

ListNode* InitNode() 
{
	ListNode* phead = CreateNode(-1);
	//让头节点的前驱和后继结点都指向自己
	phead->next = phead;	
	phead->pre = phead;
	return phead;
}

 因为修改了头结点,所以接着使用函数返回值的形式。

注意:初始化时,头结点的前驱和后继指针都指向自己。如图:

4.尾插结点

之前的单链表尾插需要遍历整个链表去找尾结点,但是,这里是带头结点的双向循环链表,只需去找头结点的前驱结点就找到了尾结点。

//尾插结点
void ListPushBack(ListNode* phead, CLDataType x) 
{
	assert(phead);
	ListNode* newnode = CreateNode(x);
	ListNode* tail = phead->pre;
	
	tail->next = newnode; //之前的尾结点的后继指针指向新结点
	newnode->pre = tail;  //新结点的前驱指向之前的尾结点
	newnode->next = phead; //新结点的后继指针指向头结点
	phead->pre = newnode;  //头结点的前驱指向

 当链表中只有一个头结点的时候,tail指向自己。

 再继续尾插时,仍是这样。

5.尾删结点

 一般都是先找到尾节结点,然后再去找到尾结点的前一个结点Pre。Pre指向头结点,头结点的前驱指向Pre。之后再删去尾节结。

//尾删结点
void ListPopBack(ListNode* phead)
{
	assert(phead);
	assert(phead->next != phead); //避免只有一个头结点
	ListNode* tail = phead->pre;
	ListNode* Pre = tail->pre; //用来记录尾结点的前一个结点
	Pre->next = phead;
	phead->pre = Pre;
	free(tail);
	tail = NULL;
}

注意:当只有一个头结点的情况时,说明已经没有链表结点。所以不能进行删除。进而这里采用,assert( phead->next != phead) 来避免此情况。

6.头插结点

和单链表的头插相似,用next指针记录头结点的下一个结点。先让新结点与next指针指向的结点建立连接,再与头结点建立连接。

//头插结点
void ListPushFront(ListNode* phead,CLDataType x) 
{
	assert(phead);
	ListNode* newnode = CreateNode(x);
	ListNode* next = phead->next; //记录头结点后的第一个结点

	newnode->next = next;
	next->pre = newnode;

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

7.头删结点 

这里需要注意的是避免只有一个头结点的情况下,删除结点,这样使用同尾删结点一样的方法,通过断言 assert(phead->next != phead);

//头删结点
void ListPopFront(ListNode* phead) 
{
	assert(phead);
	assert(phead->next != phead);
	phead->next = phead->next->next;
	phead->next->next->pre = phead;
}

8.查找并返回结点

这里采用cur指针去遍历链表,找到就返回结点,没有找到就返回空。

//查找并返回结点
ListNode* ListFind(ListNode* phead, CLDataType x) 
{
	assert(phead);
	ListNode* cur = phead->next;
	while (cur != phead) 
	{
		if (cur->val == x)
		{
			return cur;
		}
		cur = cur->next;
	}
	return NULL;
}

9.在pos结点前插入结点

因为是带头结点的双向循环链表,这样就可以直接进行插入。

//在pos前面插入结点
void ListInsert(ListNode* pos, CLDataType x) 
{
	assert(pos);
	ListNode* newnode = CreateNode(x);
    //注意顺序
    //先让后驱建立连接
	newnode->next = pos;
	pos->pre->next = newnode;
    //再前驱建立连接
	newnode->pre = pos->pre;
	pos->pre = newnode;
}

10.删除pos结点

 修改pos的前驱和后继

//删除pos位置的结点
void ListErase(ListNode* pos) 
{
	assert(pos);
	pos->pre->next = pos->next;
	pos->next->pre = pos->pre;
}

11.打印链表

//打印链表
void ListPrint(ListNode* phead) 
{
	assert(phead);
	ListNode* cur = phead->next;
	printf("phead<==>");
	while (cur != phead) 
	{
		printf("%d<==>", cur->val);
		cur = cur->next;
	}
	printf("\n");
}

 12.销毁链表

//销毁链表
void ListDestory(ListNode* phead) 
{
	assert(phead);
	ListNode* cur = phead->next;
	while (cur != phead)
	{
		ListNode* next = cur->next;
		free(cur);
		cur = next;
	}
	free(phead);
	phead = NULL;
}

13.头插结点2.0版

这里我们借助 在pos结点前插入结点 的接口。

头插结点,那就是把pos结点变为 phead->next 的结点。这样结点的插入就模拟了头插结点,从而减少了一写代码的工作量。

//头插结点2.0
void ListushFront(ListNode* phead, CLDataType x)
{
	assert(phead);
	ListNode* newnode = CreateNode(x);
	ListNode* next = phead->next; //记录头结点后的第一个结点
	ListInsert(next,x); //这里的next相当于pos结点
}

例如  :在 1结点 前插入 结点25

 

 14.尾插结点2.0版

根据带头结点的双向循环链表的特性,再借助前面的 在pos结点前插入结点(ListInsert) 的接口

这时我们会想到的就是 尾插结点不是在尾结点的后面进行插入的吗,而ListInsert是在pos前面进行结点的插入。 这时就采用  在头结点的前进行插入结点,从而达到尾插结点的特性。

//尾插结点
void ListPushBack(ListNode* phead, CLDataType x)
{
	assert(phead);
	ListNode* newnode = CreateNode(x);
	ListInsert(phead,x);
}

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

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

相关文章

Mac右键添加通过VSCode打开

Mac右键添加通过VSCode打开 1 首先打开自动操作 进入方式 访达 – 应用程序 – 自动操作 2. 选择快速操作 3. 添加 最后 commands保存&#xff0c;可以输入自定义的名称 for f in "$" doopen -a "Visual Studio Code" "$f" done4. 找到保存的快…

SpringBoot+mysql+vue实现大学生健康档案管理系统前后端分离

一、项目简介 本项目是一套基于SpringBoot实现大学生健康档案管理系统&#xff0c;主要针对计算机相关专业的正在做bishe的学生和需要项目实战练习的Java学习者。 包含&#xff1a;项目源码、数据库脚本等&#xff0c;该项目可以直接作为bishe使用。 项目都经过严格调试&#…

Java项目学生管理系统三添加学生

​ 欢迎来到本篇博客&#xff0c;昨天我们学习的是查询所有&#xff0c;今天我们将探索一个令人兴奋的话题——Java项目学生管理系统的学生添加功能。作为一个学生管理系统的关键功能之一&#xff0c;学生添加模块扮演着连接教育工作者与学生信息的桥梁。通过本文&#xff0c;我…

视频后期特效处理软件 Motion 5 mac中文版

Motion mac是一款运动图形和视频合成软件&#xff0c;适用于Mac OS平台。 Motion mac软件特点 - 精美的效果&#xff1a;Motion提供了多种高质量的运动图形和视频效果&#xff0c;例如3D效果、烟雾效果、粒子效果等&#xff0c;方便用户制作出丰富多彩的视频和动画。 - 高效的工…

还得是字节出来的,太秀了...

前段时间公司缺人&#xff0c;也面了许多测试&#xff0c;一开始瞄准的就是中级水准&#xff0c;当然也没指望能来大牛&#xff0c;提供的薪资在15-20k这个范围&#xff0c;来面试的人有很多&#xff0c;但是平均水平真的让人很失望。看了简历很多上面都是写有4年工作经验&…

(二)Tiki-taka算法(TTA)求解无人机三维路径规划研究(MATLAB)

一、无人机模型简介&#xff1a; 单个无人机三维路径规划问题及其建模_IT猿手的博客-CSDN博客 参考文献&#xff1a; [1]胡观凯,钟建华,李永正,黎万洪.基于IPSO-GA算法的无人机三维路径规划[J].现代电子技术,2023,46(07):115-120 二、Tiki-taka算法&#xff08;TTA&#xf…

zookeeper分布式先进先出队列 (实操课程)

本系列是zookeeper相关的实操课程&#xff0c;课程测试环环相扣&#xff0c;请按照顺序阅读来学习和测试zookeeper。 阅读本文之前&#xff0c;请先阅读----​​​​​​zookeeper 单机伪集群搭建简单记录&#xff08;实操课程系列&#xff09;zookeeper 客户端常用命令简单记录…

鸿蒙开发笔记

最近比较火&#xff0c;本身也是做前端的&#xff0c;就抽空学习了下。对前端很友好 原视频地址&#xff1a;黑马b站鸿蒙OS视频 下载安装跟着视频或者文档就可以了。如果你电脑上安装的有node&#xff0c;但是开发工具显示你没安装&#xff0c;不用动咱们的node&#xff0c;直…

高效复习的打印神器,学习好助手工作好搭档,咕咕机T1错题打印机上手

日常工作和学习的时候&#xff0c;经常需要打印很多电子表格和文档&#xff0c;但是手边不一定有打印机&#xff0c;对于在家或宿舍学习的学生来说更是如此&#xff0c;不过市面上常见的打印机需要墨盒、硒鼓等配件&#xff0c;使用成本高且不方便操作。那么有没有一款快捷易用…

智慧箱变监控系统

智慧箱变监控系统是一种先进的监控解决方案&#xff0c;依托电易云-智慧电力物联网&#xff0c;旨在实现对箱式变电站&#xff08;简称“箱变”&#xff09;的智能化、远程化监控和管理。以下是智慧箱变监控系统的主要功能和优势&#xff1a; 实时监测&#xff1a;通过传感器和…

ChatGPT一周年,奥特曼官宣 OpenAI 新动作!

大家好&#xff0c;我是二狗。 今天是11月30日&#xff0c;一转眼&#xff0c;ChatGPT 发布已经一周年了&#xff01; 而就在刚刚&#xff0c;ChatGPT一周年之际。 OpenAI 正式宣布Sam Altman回归重任CEO, Mira Murati 重任CTO&#xff0c;Greg Brockman重任总裁&#xff0c;O…

公共字段自动填充

1.自定义注解&#xff0c;用于表示需要进行公共字段自动填充的方法 OperationType枚举类 自定义切面类 通过反射为属性赋值

XXL-Job详解(三):任务开发

目录 前言任务配置属性任务开发bean模式1、创建任务2、调度中心新建调度任务 GLUE模式1、调度中心&#xff0c;新建调度任务&#xff1a;2、开发任务代码 前言 看该文章之前&#xff0c;最好看一下之前的文章&#xff0c;比较方便我们理解 XXL-Job详解&#xff08;一&#xf…

np.newaxis()函数的使用

import numpy as np x np.array([2, 4, 7, 9, 20]) print(x的形状:, x.shape) # x的形状: (5,) print(x的值:, x) # x的值: [ 2 4 7 9 20]x_new1 x[np.newaxis, :] # (5,) -----》 (1, 5) print(x_new1的形状:, x_new1.shape) # x_new1的形状: (1, 5) print…

【Openstack Train安装】十二、Cinder安装

Cinder在块存储资源和计算服务&#xff08;Nova&#xff09;之间提供了一个抽象层。通过Cinder API&#xff0c;块存储可以被管理&#xff08;创建、销毁和分配等&#xff09;&#xff0c;而不需要知道提供存储的底层资源。 本文介绍Cinder安装步骤&#xff0c;Cinder需在控制节…

Visual Studio通过ClaudiaIDE插件设置背景图片

首先&#xff0c;在VS菜单栏上选择扩展-管理扩展&#xff0c;搜索插件为 ClaudiaIDE&#xff0c; 下载完成之后&#xff0c;关闭VS&#xff0c;点击Modify按钮安装&#xff1a; 等待安装完成&#xff0c;进入 VS , 打开 工具----选项---- ClauDiaIDE 界面 这个是背景色调 我选的…

免费用的 AI 工具 CodeGeeX ,开发效率翻倍!!!

其他系列文章导航 Java基础合集数据结构与算法合集 设计模式合集 多线程合集 分布式合集 ES合集 文章目录 其他系列文章导航 文章目录 前言 一、AI使用教程 二、工作提效 2.1 AI 导师 2.2 AI 程序助手 2.3 AI 测试员 2.4 AI Bug 修复师 三、其他能力 四、总结 前言…

实现简单的Http服务器+SpringMvc,集成到Spring

实现简单的Http服务器SpringMvc&#xff0c;集成到Spring 1、Http协议 1.1、HTTP 协议请求格式 方法 空格 URL 空格 版本 回车符 换行符头部域名称&#xff1a;头部域值 回车符 换行符...头部域名称&#xff1a;头部域值 回车符 …

次世代建模纹理贴图怎么做?

在线工具推荐&#xff1a; 三维数字孪生场景工具 - GLTF/GLB在线编辑器 - Three.js AI自动纹理化开发 - YOLO 虚幻合成数据生成器 - 3D模型在线转换 - 3D模型预览图生成服务 1、什么是次时代建模&#xff1f; "次世代建模"是一个术语&#xff0c;通常用来描述…

daima8资源网整站数据打包完整代码(集成了ripro9.1主题,开箱即用)

基于ripro9.1完全明文无加密后门版本定制开发&#xff0c;无需独立服务器&#xff0c;虚拟主机也可以完美运营&#xff0c;只要主机支持php和mysql即可。整合了微信登录和几款第三方的主题文件&#xff0c;看起来更美观一些。站长本人就是程序员&#xff0c;所以本站的代码资源…