链表基础4——带头双向循环链表

news2024/9/21 12:21:51

什么是带头双向循环链表

我们直接看图片

定义结点类型 

typedef int LTDataType;//存储的数据类型

typedef struct ListNode
{
	LTDataType data;//数据域
	struct ListNode* prev;//前驱指针
	struct ListNode* next;//后继指针
}ListNode;

链表的初始化 

//创建一个新结点
ListNode* BuyListNode(LTDataType x)
{
	ListNode* node = (ListNode*)malloc(sizeof(ListNode));
	if (node == NULL)
	{
		printf("malloc fail\n");
		exit(-1);
	}
	node->data = x;//新结点赋值
	node->prev = NULL;
	node->next = NULL;

	return node;//返回新结点
}

//初始化链表
ListNode* ListInit()
{
	ListNode* phead = BuyListNode(-1);//申请一个头结点,头结点不存储有效数据
	//起始时只有头结点,让它的前驱和后继都指向自己
	phead->prev = phead;
	phead->next = phead;
	return phead;//返回头结点
}

销毁链表

销毁链表,从头结点的后一个结点处开始向后遍历并释放结点,直到遍历到头结点时,停止遍历并将头结点也释放掉。

//销毁链表
void ListDestroy(ListNode* phead)
{
	assert(phead);

	ListNode* cur = phead->next;//从头结点后一个结点开始释放空间
	ListNode* next = cur->next;//记录cur的后一个结点位置
	while (cur != phead)
	{
		free(cur);
		cur = next;
		next = next->next;
	}
	free(phead);//释放头结点
}

打印双向链表 

打印双向链表时也是从头结点的后一个结点处开始向后遍历并打印,直到遍历到头结点处时便停止遍历和打印(头结点数据不打印)。

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

	ListNode* cur = phead->next;//从头结点的后一个结点开始打印
	while (cur != phead)//当cur指针指向头结点时,说明链表以打印完毕
	{
		printf("%d ", cur->data);
		cur = cur->next;
	}
	printf("\n");
}

查找元素

给定一个值,在链表中寻找与该值相同的结点,若找到了,则返回结点地址;若没有找到,则返回空指针(NULL)。

//查找元素
ListNode* ListFind(ListNode* phead, LTDataType x)
{
	assert(phead);

	ListNode* cur = phead->next;//从头结点的后一个结点开始查找
	while (cur != phead)//当cur指向头结点时,说明链表已遍历完毕
	{
		if (cur->data == x)
		{
			return cur;//返回目标结点的地址
		}
		cur = cur->next;
	}
	return NULL;//没有找到目标结点
}

增加结点

头插

 头插,即申请一个新结点,将新结点插入在头结点和头结点的后一个结点之间即可。

//头插
void ListPushFront(ListNode* phead, LTDataType x)
{
	assert(phead);

	ListNode* newnode = BuyListNode(x);//申请一个结点,数据域赋值为x
	ListNode* front = phead->next;//记录头结点的后一个结点位置
	//建立新结点与头结点之间的双向关系
	phead->next = newnode;
	newnode->prev = phead;
	//建立新结点与front结点之间的双向关系
	newnode->next = front;
	front->prev = newnode;
}

 尾插

 尾插,申请一个新结点,将新结点插入到头结点和头结点的前一个结点之间即可。因为链表是循环的,头结点的前驱指针直接指向最后一个结点,所以我们不必遍历链表找尾。

//尾插
void ListPushBack(ListNode* phead, LTDataType x)
{
	assert(phead);

	ListNode* newnode = BuyListNode(x);//申请一个结点,数据域赋值为x
	ListNode* tail = phead->prev;//记录头结点的前一个结点的位置
	//建立新结点与头结点之间的双向关系
	newnode->next = phead;
	phead->prev = newnode;
	//建立新结点与tail结点之间的双向关系
	tail->next = newnode;
	newnode->prev = tail;
}

在指定位置插入结点

 在直到位置插入结点,准确来说,是在指定位置之前插入一个结点。我们只需申请一个新结点插入到指定位置结点和其前一个结点之间即可

//在指定位置插入结点
void ListInsert(ListNode* pos, LTDataType x)
{
	assert(pos);

	ListNode* before = pos->prev;//记录pos指向结点的前一个结点
	ListNode* newnode = BuyListNode(x);//申请一个结点,数据域赋值为x
	//建立新结点与before结点之间的双向关系
	before->next = newnode;
	newnode->prev = before;
	//建立新结点与pos指向结点之间的双向关系
	newnode->next = pos;
	pos->prev = newnode;
}

删除结点

头删

 头删,即释放头结点的后一个结点,并建立头结点与被删除结点的后一个结点之间的双向关系即可。

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

	ListNode* front = phead->next;//记录头结点的后一个结点
	ListNode* newfront = front->next;//记录front结点的后一个结点
	//建立头结点与newfront结点之间的双向关系
	phead->next = newfront;
	newfront->prev = phead;
	free(front);//释放front结点
}

尾删

 尾删,即释放最后一个结点,并建立头结点和被删除结点的前一个结点之间的双向关系即可。

//尾删
void ListPopBack(ListNode* phead)
{
	assert(phead);
	assert(phead->next != phead);

	ListNode* tail = phead->prev;//记录头结点的前一个结点
	ListNode* newtail = tail->prev;//记录tail结点的前一个结点
	//建立头结点与newtail结点之间的双向关系
	newtail->next = phead;
	phead->prev = newtail;
	free(tail);//释放tail结点
}

删除指定位置结点

删除指定位置结点,释放掉目标结点后,建立该结点前一个结点和后一个结点之间的双向关系即可。

//删除指定位置结点
void ListErase(ListNode* pos)
{
    assert(pos);

    ListNode* before = pos->prev;//记录pos指向结点的前一个结点
    ListNode* after = pos->next;//记录pos指向结点的后一个结点
    //建立before结点与after结点之间的双向关系
    before->next = after;
    after->prev = before;
    free(pos);//释放pos指向的结点
}

链表判空

 链表判空,即判断头结点的前驱或是后驱指向的是否是自己即可。

//链表判空
bool ListEmpty(ListNode* phead)
{
	assert(phead);

	return phead->next == phead;//当链表中只有头结点时为空
}

获取链表中的元素个数

 获取链表中的元素个数,即遍历一遍链表,统计结点的个数(头结点不计入)并返回即可。

//获取链表中的元素个数
int ListSize(ListNode* phead)
{
	assert(phead);

	int count = 0;//记录元素个数
	ListNode* cur = phead->next;//从头结点的后一个结点开始遍历
	while (cur != phead)//当cur指向头结点时,遍历完毕,头结点不计入总元素个数
	{
		count++;
		cur = cur->next;
	}
	return count;//返回元素个数
}

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

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

相关文章

Java 非对称加密RSA应用实现

1.RSA介绍 RSA算法是一种非对称加密算法,与对称加密算法不同的是,RSA算法有两个不同的密钥,一个是公钥,一个是私钥。 RSA公开密钥密码体制是一种使用不同的加密密钥与解密密钥,“由已知加密密钥推导出解密密钥在计算上是不可行的…

王者荣耀快速提升等级

1、为什么要提升等级 等级越高,解锁的玩法越多 等级越高,解锁的铭文数量越多,铭文能提升英雄的技能属性 2、如何快速提升等级 通过快速赛、排位赛、人机赛等均可以获取经验值 通过经验翻倍卡,可以快速获取经验值 3、经验卡翻倍…

YOLOv8水稻病害检测系统(python代码,可以通过图片、视频或者摄像头三种路径进行检测)

1.效果视频:最新最全面的水稻病害检测创作(yolov8模型,稻瘟病、纹枯病、褐斑病、枯心病、霜霉病、水稻细菌性条纹斑病、稻苞虫等病害。)_哔哩哔哩_bilibili 2.数据集介绍 水稻叶病害数据集(目标检测,yolo…

信道的题目

调制信道分为恒参信道和随参信道。恒参信道举例:各种有线信道;中长波地波传播、卫星中继。随参信道举例:短波电离层反射信道、各种散射信道、移动通信信道。狭义信道分为有线信道和无线信道。广义信道包含调制信道和编码信道。调制信道中不包…

RK3568 android11 修改关机弹窗界面

需要修改关机弹窗界面&#xff0c;当前界面我已经按照客户需求去掉emergency 但是客户需要按其他区域可以实现返回&#xff0c;也就是点击黑色背景取消dialog 嗑代码发现黑色布局为&#xff1a; <node index"0" text"" resource-id"com.android.…

【R语言】混合图:小提琴图+箱线图

{ggstatsplot} 是 {ggplot2} 包的扩展&#xff0c;用于创建图形&#xff0c;其中包含信息丰富的绘图本身中包含的统计测试的详细信息。在典型的探索性数据分析工作流程中&#xff0c;数据可视化和统计建模是两个不同的阶段&#xff1a;可视化通知建模&#xff0c;而建模又可以建…

janus架构学习

基础介绍 Janus 是由Meetecho设计和开发的开源、通用的基于SFU架构的WebRTC流媒体服务器&#xff0c;它支持在Linux的服务器或MacOS上的机器进行编译和安装。Janus 是使用C语言进行编写的&#xff0c;它的性能十分优秀。 架构 janus为sfu架构 模块结构图 模块说明 core模…

C# Solidworks二次开发:获取模型、组件、主体的表面积相关API详解

大家好&#xff0c;今天要介绍模型、组件、主体的表面积相关API。 下面是今天要介绍的API: &#xff08;1&#xff09;第一个为SurfaceArea Property (IMassProperty)&#xff0c;这个API的含义为获取此模型的表面积&#xff0c;下面是官方的具体解释&#xff1a; 其没有输入…

如何解决DDoS攻击?群联科技做出回答。

DDoS攻击&#xff08;分布式拒绝服务攻击&#xff09;是一种恶意利用多台傀儡机协同发起大规模网络流量&#xff0c;旨在压垮目标系统或网络资源&#xff0c;使其无法正常服务的网络攻击手段。由于现代计算机和网络性能的提升&#xff0c;单点发起的DoS攻击已难以奏效&#xff…

UML 介绍

前言 UML 简介。 文章目录 前言一、简介1、事务2、关系1&#xff09;依赖2&#xff09;关联聚合组合 3&#xff09;泛化4&#xff09;实现 二、类图三、对象图四、用例图五、交互图1、序列图&#xff08;顺序图&#xff09;2、通信图 六、状态图七、活动图八、构件图&#xff0…

探索设计模式的魅力:开启智慧之旅,AI与机器学习驱动的微服务设计模式探索

​&#x1f308; 个人主页&#xff1a;danci_ &#x1f525; 系列专栏&#xff1a;《设计模式》 &#x1f4aa;&#x1f3fb; 制定明确可量化的目标&#xff0c;坚持默默的做事。 ✨欢迎加入探索AI与机器学习驱动的微服务设计模式之旅✨ 亲爱的科技爱好者们&#xff0c;有没…

C#语法知识之运算符

3、运算符 目录 3、运算符1、算数运算符思考 秒转化时间 2、字符串拼接3、条件运算符4、逻辑运算符5、位运算符6、三目运算符思考 闰年 1、算数运算符 1、赋值符号 //把右侧的值赋给左侧的变量2、算数运算符 _ * / float f 1 / 2f; %3、算数运算符的优先级 //乘除余优先级高…

Python可视化数据分析-饼状图

一、前言 饼状图&#xff08;Pie Chart&#xff09;是一种常用的数据可视化图表&#xff0c;用于展示数据中各部分的占比关系。Python 中有多种库可以用于绘制饼状图&#xff0c;比较常用的包括 matplotlib、pyecharts和 plotly 等。 二、使用 matplotlib 绘制饼状图 import…

C++ 深入理解 继承

本篇文章将谈谈一下几个问题&#xff1a; 1.基类和派生类对象赋值转换 2.继承中的作用域 3.派生类的默认成员函数 4.复杂的菱形继承及菱形虚拟继承 5.其他 1.基类和派生类对象赋值转换 1.派生类对象 可以赋值给 基类的对象 / 基类的指针 / 基类的引用。这里有个形象的说法叫切…

MySQL-实验-单表、多表数据查询和嵌套查询

目录 0.简单子查询 &#xff08;1&#xff09;带比较运算符的子查询 &#xff08;2&#xff09;关键字子查询 1.多表查询 3.子查询 4.多表子查询 0.简单子查询 &#xff08;1&#xff09;带比较运算符的子查询 在右侧编辑器补充代码&#xff0c;查询大于所有平均年龄的员…

镜像仓库registory中的秘钥管理

镜像仓库中的秘钥管理 1 生成密钥 在使用私有镜像拉取镜像时&#xff0c;需要为私有镜像仓库创建一个镜像仓库的密钥&#xff0c;并在创建容器中进行引用。创建镜像仓库的语法和格式&#xff1a; kubectl create secret docker–registry <regsecret-name> —docker–…

C/C++程序设计实验报告4 | 函数实验

本文整理自博主本科大一《C/C程序设计》专业课的课内实验报告&#xff0c;适合C语言初学者们学习、练习。 编译器&#xff1a;gcc 10.3.0 ---- 注&#xff1a; 1.虽然课程名为C程序设计&#xff0c;但实际上当时校内该课的内容大部分其实都是C语言&#xff0c;C的元素最多可能只…

kali下设置root权限(包含很多技巧)

默认是没有密码的&#xff0c;后期使用超级管理员root操作kali系统&#xff0c;我们需要设置 Ctrlshift再加上“”号可以调节字体大小 此时提示符为#

漂亮的个人主页源码

源码介绍 漂亮的个人主页源码&#xff0c;源码由HTMLCSSJS组成&#xff0c;记事本打开源码文件可以进行内容文字之类的修改&#xff0c;双击html文件可以本地运行效果&#xff0c;也可以上传到服务器里面&#xff0c;重定向这个界面 效果截图 源码下载 漂亮的个人主页源码

Taro-vue微信小程序用户隐私保护

Taro-vue微信小程序用户隐私保护 一、在 微信公众平台的【设置】- 【服务内容与声明】 &#xff0c;设置用户隐私保护指引&#xff0c;添加项目需要的接口权限。 【用户隐私保护指引】提交之后&#xff0c;官方会进行审核。审核通过之后&#xff0c;对应的接口权限才会生效。 …