详解带头双向循环列表

news2024/11/28 8:36:50

目录

前言

一、带头双向循环链表的结构

二、 带头双向循环链表的实现

2.1链表的创建

2.2开辟新的结点

2.3初始化

2.4释放销毁

2.5链表的打印

2.7尾插

2.8尾删

2.9头插

2.10头删

三、带头双向循环链表中间随机值的插入和删除

3.1在pos位置插入x

3.2删除pos位置的值

四、完整代码


前言

上篇文章我们讲述了链表,链表有带头、不带头,循环、不循环,单向、双向,三种大的形式这三种形式可以组合出8中结构,今天我们学习最常用的其中一种——带头双向循环链表。

一、带头双向循环链表的结构

我们可以看出这个结构和上篇文章的无头单向非循环链表最大的区别是含有一个头部和每个结构内都含有两个指针,一个指向下个结构,一个指向前一个结构。

二、 带头双向循环链表的实现

2.1链表的创建

typedef int SLTDatatype;
typedef struct SListNode
{
	SLTDatatype data;
	struct SListNode* next;
	struct SListNode* prev;
}SLTNode;

从这个结构体我们就可以看到里面含有两个指针一个next指向下一个,一个prev指向前一个。

2.2开辟新的结点

SLTNode* BuySLT( SLTDatatype x)
{
	SLTNode* newnode = (SLTNode*)malloc(sizeof(SLTNode));
	if (newnode==NULL)
	{
		perror("malloc failed");
		exit(-1);
	}
	newnode->data = x;
	newnode->next = NULL;
	newnode->prev = NULL;
	return newnode;
}

2.3初始化

SLTNode* SLTInit()
{
	SLTNode* phead = BuySLT(0);
	phead->next = phead;
	phead->prev = phead;
	return phead;
}

这个初始化会创建一个头,我们这个头是循环的,要进行的增删查改的操作也是以这个头为根基进行操作的。

2.4释放销毁

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

我们要使用malloc函数开辟空间,在程序结束时也要依次释放,否则会造成内存泄漏。

2.5链表的打印

void SLprint(SLTNode*phead)
{
	SLTNode* tail = phead->next;
	printf("phead<=>");
	while (tail!= phead)
	{
		printf("%d<=>", tail->data);
		tail = tail->next;
	}
	printf("\n");
}

2.6查找链表中的数据

SLTNode* SLFind(SLTNode* phead, SLTDatatype x)
{
	assert(phead);
	SLTNode* cur = phead->next;
	while (cur!= phead)
	{
		if (cur->data == x)
		{
			return cur;
		}
		else
		{
			cur = cur->next;
		}
	}
	return NULL;
}

由于我们的链表是循环的不能以结尾指向空的指针结尾,要根据这个结构以不指向头的指针为判断条件创建循环,依次打印每个结构中的有效数据。

2.7尾插

void SLPushBack(SLTNode* phead, SLTDatatype x)
{
	assert(phead);
	SLTNode* newnode = BuySLT(x);
	SLTNode* tail = phead->prev;
	newnode->prev = tail;
	tail->next = newnode;

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


}

根据这个链表的结构特点,我们可以通过头指针里的指向后一个也就是末尾的结构体,然后将末尾的结构和新结点里的两个指针连接起来,再将新节点和头结点链接起来,尾插就完成了,整个结构也就串联起来了。

2.8尾删

void SLPopBack(SLTNode* phead)
{
	assert(phead);
	if (phead->next != phead)
	{
		SLTNode* tail = phead->prev;
		SLTNode* first = tail->prev;

		first->next = phead;
		phead->prev = first;
		free(tail);
	}

}

通过头指针里的指向后一个指针就可以找到链表的尾,再根据链表的尾找到前一个保存前一个释放掉尾,然后再将其和头串联起来。

2.9头插

void SLPushFront(SLTNode* phead,SLTDatatype x)
{
	assert(phead);
	SLTNode* newnode = BuySLT(x);
	SLTNode* first = phead->next;
	newnode->next = first;
	first->prev = newnode;

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

}

还是根据这个链表的结构特点就很容易完成头插的操作

2.10头删

void SLPopFront(SLTNode* phead)
{
	assert(phead);
	SLTNode* first = phead->next;
	SLTNode* cur = first->next;
	cur->prev = phead;
	phead->next = cur;
	free(first);
}

和前面的操作大差不差根据这个链表的特点完成这个操作

三、带头双向循环链表中间随机值的插入和删除

这个附加内容就不详解了,通过前面的文章大家应该能有自己的思路了吧!给大家个参考。

3.1在pos位置插入x

void SLTInsert( SLTNode* pos, SLTDatatype x)
{

	assert(pos);
	SLTNode* posprev = pos->prev;
	SLTNode* newnode = BuySLT(x);
	posprev->next = newnode;
	newnode->prev = posprev;
	newnode->next = pos;
	pos->prev = newnode;

}

3.2删除pos位置的值

void SLTErase(SLTNode* pos)
{
	assert(pos);
	SLTNode* first = pos->prev;
	SLTNode* cur = pos->next;

	first->next = cur;
	cur->prev = first;
	free(pos);
}

四、完整代码

#define _CRT_SECURE_NO_WARNINGS 67
#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
typedef int SLTDatatype;
typedef struct SListNode
{
	SLTDatatype data;
	struct SListNode* next;
	struct SListNode* prev;
}SLTNode;
SLTNode* BuySLT( SLTDatatype x)
{
	SLTNode* newnode = (SLTNode*)malloc(sizeof(SLTNode));
	if (newnode==NULL)
	{
		perror("malloc failed");
		exit(-1);
	}
	newnode->data = x;
	newnode->next = NULL;
	newnode->prev = NULL;
	return newnode;
}
void SLTDer(SLTNode* phead)
{
	SLTNode* cur = phead->next;
	while (cur != phead)
	{
		SLTNode* next = cur->next;
		free(cur);
		cur = next;
	}
	free(phead);
}
SLTNode* SLTInit()
{
	SLTNode* phead = BuySLT(0);
	phead->next = phead;
	phead->prev = phead;
	return phead;
}
void SLprint(SLTNode*phead)
{
	SLTNode* tail = phead->next;
	printf("phead<=>");
	while (tail!= phead)
	{
		printf("%d<=>", tail->data);
		tail = tail->next;
	}
	printf("\n");
}
SLTNode* SLFind(SLTNode* phead, SLTDatatype x)
{
	assert(phead);
	SLTNode* cur = phead->next;
	while (cur!= phead)
	{
		if (cur->data == x)
		{
			return cur;
		}
		else
		{
			cur = cur->next;
		}
	}
	return NULL;
}
//尾插
void SLPushBack(SLTNode* phead, SLTDatatype x)
{
	assert(phead);
	SLTNode* newnode = BuySLT(x);
	SLTNode* tail = phead->prev;
	newnode->prev = tail;
	tail->next = newnode;

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


}
//尾删
void SLPopBack(SLTNode* phead)
{
	assert(phead);
	if (phead->next != phead)
	{
		SLTNode* tail = phead->prev;
		SLTNode* first = tail->prev;

		first->next = phead;
		phead->prev = first;
		free(tail);
	}

}
//头插
void SLPushFront(SLTNode* phead,SLTDatatype x)
{
	assert(phead);
	SLTNode* newnode = BuySLT(x);
	SLTNode* first = phead->next;
	newnode->next = first;
	first->prev = newnode;

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

}
//头删
void SLPopFront(SLTNode* phead)
{
	assert(phead);
	SLTNode* first = phead->next;
	SLTNode* cur = first->next;
	cur->prev = phead;
	phead->next = cur;
	free(first);
}
//
void SLTInsert( SLTNode* pos, SLTDatatype x)
{

	assert(pos);
	SLTNode* posprev = pos->prev;
	SLTNode* newnode = BuySLT(x);
	posprev->next = newnode;
	newnode->prev = posprev;
	newnode->next = pos;
	pos->prev = newnode;

}
//
void SLTErase(SLTNode* pos)
{
	assert(pos);
	SLTNode* first = pos->prev;
	SLTNode* cur = pos->next;

	first->next = cur;
	cur->prev = first;
	free(pos);
}
int main()
{
	SLTNode* plist = SLTInit();
	//尾插
	SLPushBack(plist, 1);
	SLPushBack(plist, 2);
	SLPushBack(plist, 3);
	SLPushBack(plist, 4);
	SLPushBack(plist, 5);
	//尾插测试
	printf("尾插:\n");
	SLprint(plist);
	//尾删
	SLPopBack(plist);
	printf("尾删:\n");
	SLprint(plist);
	//头插
	SLPushFront(plist,10);
	printf("头插:\n");
	SLprint(plist);
	//头删
	SLPopFront(plist);
	printf("头删:\n");
	SLprint(plist);
	//在pos位置后插入x
	SLTNode* pos1 = SLFind(plist, 4);
	SLTInsert(pos1, 5);
	printf("在pos位置插入x:\n");
	SLprint(plist);
	//删除pos位置的值
	SLTNode* pos2 = SLFind(plist, 5);
	SLTErase(pos2);
	printf("删除pos位置的值\n");
	SLprint(plist);
	SLTDer(plist);
	plist = NULL;
	return 0;
}

这个就是本篇文章的所有内容了,通读全文我们可以发现带头双向循环链表这个结构确实实用,使用起来也是非常的方便简洁,数据结构链表的内容到这就基本结束了,欢迎大家在评论区留言,交流和探讨,说出自己的想法。

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

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

相关文章

C#回调函数学习1

回调函数&#xff08;Callback Function&#xff09;是一种函数指针&#xff0c;它指向的是由用户自己定义的回调函数。我们将这个回调函数的指针作为参数传递给另外一个函数&#xff0c;在这个函数工作完成后&#xff0c;它将通过这个回调函数的指针来回调通知调用者处理结果。…

XREAL 联合创始人吴克艰谈AR:下一代计算平台及其关键技术

// 编者按&#xff1a;一种行业观点是&#xff0c;AR或是未来十年、三十年的革命性技术&#xff0c;是下一代计算平台。近半个世纪&#xff0c;我们总能听到苹果在AR行业的创新动作&#xff0c;开辟了新的硬件范式。AR/VR行业为苹果不断欢呼的同时&#xff0c;激发了人们的好…

JAVA设计模式8:装饰模式,动态地将责任附加到对象上,扩展对象的功能

作者主页&#xff1a;Designer 小郑 作者简介&#xff1a;3年JAVA全栈开发经验&#xff0c;专注JAVA技术、系统定制、远程指导&#xff0c;致力于企业数字化转型&#xff0c;CSDN博客专家&#xff0c;阿里云社区专家博主&#xff0c;蓝桥云课讲师。 目录 一、什么是装饰模式二、…

linux安装Sentinal1.8.6

前言&#xff1a; 使用docker search sentinel-dashboard命令&#xff0c;发现docker中的镜像版本过低&#xff0c;由于要配合使用1.8.6&#xff0c;所以这里采用java后台运行sentinel1.8.6-jar的方式。 1、官网下载对应版本jar&#xff08;https://github.com/alibaba/Sentin…

【Unity编辑器扩展】| 自定义窗口和面板

前言【Unity编辑器扩展】| 自定义窗口和面板一、EditorWindow二、ScriptableWizard三、编辑器绘制3.1 文本输入3.2 空行3.3 滑动条、进度条3.4 枚举选择3.5 其他总结前言 前面我们介绍了Unity中编辑器扩展的一些基本概念及基础知识,还有编辑器扩展中用到的相关特性Attribute介…

招募 AIGC 训练营助教 @上海

诚挚邀请对社区活动感兴趣的你&#xff0c;成为我们近期开展的训练营助教。 与我们共同开启这场创新之旅&#xff01; 助教需要参与&#xff1a; 协助策划和组织训练营活动 协助招募和筛选学员 协助制定训练营的宣传方案 负责协调和组织各项活动 助教可获得&#xff1a; AIGC知…

vue cli 打包、生产环境http-proxy-middleware代理

结构树 版本 1、创建vue.config.js const path require(path); const UglifyJsPlugin require(uglifyjs-webpack-plugin) //压缩 const CompressionWebpackPlugin require(compression-webpack-plugin) const isProduction process.env.NODE_ENV ! development;module.exp…

C#控制台程序中使用log4.net来输出日志

Apache log4net 库是一个帮助程序员将日志语句输出到各种输出目标的工具。log4net 是优秀的 Apache log4j™ 框架到 Microsoft .NE​​T 运行时的端口。 我喜欢他可以自定义输出&#xff0c;区分等级等特点。 导入库 我们在工程里添加NuGet的包。输入名称log4net &#xff0…

【Transformer系列】深入浅出理解Positional Encoding位置编码

一、参考资料 一文教你彻底理解Transformer中Positional Encoding Transformer Architecture: The Positional Encoding The Annotated Transformer Master Positional Encoding: Part I 如何理解Transformer论文中的positional encoding&#xff0c;和三角函数有什么关系&…

05ShardingSphere-JDBC水平分片

1、准备服务器 随着业务的扩大&#xff0c;订单表数据量不断增加&#xff0c;数据库面临存储压力&#xff0c;开始考虑对订单表进行水平分片。 将t_order表扩展为server-order0中的t_order0和t_order1、server-order1中的t_order0和t_order1 服务器规划&#xff1a;使用dock…

使用Python创建音乐播放器

1. 介绍 在本篇博客中&#xff0c;我们将介绍如何使用Python编程语言和wxPython模块创建一个简单的音乐播放器。我们将使用wxPython来构建用户界面&#xff0c;并借助pygame模块来实现音频播放的功能。 C:\pythoncode\new\quickplaywav.py 2. 使用方法 使用我们提供的源代码…

排查disabled问题之谷歌新版本特性

问题复现 最近我突然接手一个后台的bug&#xff0c;这个后台很久没有迭代更新了&#xff0c;我也不熟悉业务&#xff0c;所以只能看一下源码&#xff0c;问题很快就复现&#xff0c;测试的修复操作也很正确&#xff0c;就是因为渲染的input标签中存在disableddisabled’属性导…

2023 年您需要了解哪些类型的数据泄露?

到目前为止&#xff0c;所有公司都应该意识到网络安全威胁是任何企业面临的主要风险之一。其中&#xff0c;那些直接损害敏感数据的行为可能会造成特别严重的破坏。 目前&#xff0c;数据泄露的典型成本已接近 450 万美元&#xff08;在过去三年中增加了 15%&#xff09;&…

MySQL内外连接、索引特性

目录 内连接 外连接 索引特性 理解索引 删除索引 MySQL内外连接是一种用于联接两个或多个表的操作。内连接只返回满足连接条件的行&#xff0c;外连接返回满足条件和不满足条件的行。 内连接 SQL如下&#xff1a; SELECT ... FROM t1 INNER JOIN t2 ON 连接条件 [INNER …

使用香橙派 学习Linux的串口开发

串口的回顾 & 硬件接线 关于串口也是之前学习过很多次了&#xff0c;详见&#xff1a; 认识串口 和 蓝牙模块HC08_hc08蓝牙模块_mjmmm的博客-CSDN博客 串口的再认识-CSDN博客 香橙派提供了两路串口&#xff0c;第一路就是在刷机时串口连接的引脚&#xff08;对应驱动ttyS0&…

input标签,新增那些属性

input标签作为页面与用户交互的重要入口&#xff0c;了解掌握input的属性&#xff0c;至为重要。 type属性 HTML5给input表现的type属性&#xff0c;添加了很多的属性值&#xff0c;用来丰富了文本框类型。比如&#xff1a; <body><input type"email" na…

前端--HTML

文章目录 HTML结构快速生成代码框架HTML常见标签 表格标签 编写简历信息 填写简历信息 Emmet 快捷键 HTML 特殊字符 一、HTML结构 1.认识HTML标签 HTML 代码是由 "标签" 构成的. 形如: <body>hello</body> 标签名 (body) 放到 < > 中 大部分标…

Spring框架中的@Conditional系列注解

目录 1 Contidional 介绍1.1 Condition 接口1.2 Spring Conditional注解实例1.3 Conditional 与Profile 的对比 2 Spring boot 扩展2.1 ConditionalOnClass和ConditionalOnMissingClass注解2.2 ConditionalOnBean 和ConditionalOnMissingBean注解2.3 ConditionalOnProperty注解…

(二十八)大数据实战——Flume数据采集之kafka数据生产与消费集成案例

前言 本节内容我们主要介绍一下flume数据采集和kafka消息中间键的整合。通过flume监听nc端口的数据&#xff0c;将数据发送到kafka消息的first主题中&#xff0c;然后在通过flume消费kafka中的主题消息&#xff0c;将消费到的消息打印到控制台上。集成使用flume作为kafka的生产…

18. 线性代数 - 线性变换

文章目录 线性空间线性变换线性变换的几何意义特征值与特征向量NumPy的矩阵操作Hi, 你好。我是茶桁。 经历了几节线性代数课程之后,终于咱们到了最后一节课了。本节课的内容说多不多,说少也不少。 我们先是要理解一下线性空间和线性变换,并且探讨一下线性变换的几何意义。…