【数据结构】带头双向循环链表基本操作的实现(C语言)

news2025/7/11 18:31:01

🚀 作者简介:一名在后端领域学习,并渴望能够学有所成的追梦人。
🐌 个人主页:蜗牛牛啊
🔥 系列专栏:🛹初出茅庐C语言、🛴数据结构
📕 学习格言:博观而约取,厚积而薄发
🌹 欢迎进来的小伙伴,如果小伙伴们在学习的过程中,发现有需要纠正的地方,烦请指正,希望能够与诸君一同成长! 🌹


文章目录

  • 一、概念与结构
  • 二、基本操作的实现
    • 1.创建结点
    • 2.初始化链表
    • 3.打印链表
    • 4.尾插
    • 5.尾删
    • 6.头插
    • 7.头删
    • 8.查找某个数并返回其指针
    • 9.在某个位置之前插入
    • 10.删除某个位置
    • 11.判断链表是否为空
    • 12.计算链表中有效值的个数
    • 13.销毁链表
  • 三、测试代码

一、概念与结构

无头单向非循环链表结构简单,一般不会单独用来存数据。实际中更多的是作为其他数据结构的子结构,如哈希桶、图的邻接表等等。而带头双向循环链表的结构较为复杂,一般用在单独存储数据。实际中使用的链表数据结构,都是带头双向循环链表,虽然它的结构复杂但是因为结构优势用代码实现起来会变得简单。

结构

二、基本操作的实现

1.创建结点

LTNode* BuyListNode(ListDataType x)
{
	LTNode* newnode = (LTNode*)malloc(sizeof(LTNode));
	if (newnode == NULL)
	{
		perror("malloc");
		exit(-1);
	}
	newnode->val = x;
	newnode->prev = NULL;
	newnode->next = NULL;
	return newnode;
}

2.初始化链表

void ListInit(LTNode** pphead)//初始化
{
	*pphead = (LTNode*)malloc(sizeof(LTNode));
	*pphead = BuyListNode(-1);
	(*pphead)->next = *pphead;
	(*pphead)->prev = *pphead;
}
//LTNode* ListInit()//初始化
//{
//	LTNode* phead = BuyListNode(-1);//初始化时将头结点置为-1;
//	phead->next = phead;
//	phead->prev = phead;
//	return phead;
//}

初始化链表有两种方式,一种是有返回类型(注释部分),另一种是在初始化函数中给结构体开辟空间(不过注意参数要为二级指针)。因为是带头结点的循环链表,所以prevnext指针都指向自己。

3.打印链表

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

注意while循环的结束条件,保证能够打印链表中的所有有效值。要对头结点进行assert判断,不能为空。

4.尾插

void ListPushBack(LTNode* phead, ListDataType x)//尾插
{
	assert(phead);
	LTNode* newnode = BuyListNode(x);
	LTNode* tail = phead->prev;
	newnode->next = tail->next;
	phead->prev = newnode;
	newnode->prev = tail;
	tail->next = newnode;
	//ListInsert(phead, x);

}

5.尾删

void ListPopBack(LTNode* phead)//尾删
{
	assert(phead);
	assert(phead->next != phead);
	LTNode* prevnode = phead->prev;
	prevnode->prev->next = phead;
	phead->prev = prevnode->prev;
	free(prevnode);
	//ListErase(phead->prev);
}

尾删时要注意判断phead->next != phead,不能删除头结点,同时记得要free(prevnode)释放删除后的空间。

6.头插

void ListPushFront(LTNode* phead, ListDataType x)//头插
{
	assert(phead);
	LTNode* tail = phead->next;
	LTNode* newnode = BuyListNode(x);
	tail->prev = newnode;
	newnode->next = tail;
	newnode->prev = phead;
	phead->next = newnode;
	//ListInsert(phead->next,x);
}

7.头删

void ListPopFront(LTNode* phead)//头删
{
	assert(phead);
	assert(phead->next != phead);
	LTNode* tail = phead->next;
	phead->next = tail->next;
	tail->next->prev = phead;
	//ListErase(phead->next);
}

8.查找某个数并返回其指针

LTNode* ListFind(LTNode* phead, ListDataType x)//找某个数返回其指针
{
	assert(phead);
	LTNode* cur = phead->next;
	while (cur != phead)
	{
		if (cur->val == x)
		{
			return cur;
		}
		cur = cur->next;
	}
	return NULL;
}

9.在某个位置之前插入

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

10.删除某个位置

void ListErase(LTNode* pos)//删除pos位置
{
	assert(pos);
	LTNode* prevnode = pos->prev;
	LTNode* nextnode = pos->next;
	free(pos);
	prevnode->next = nextnode;
	nextnode->prev = prevnode;
	/*pos->next->prev = pos->prev;
	pos->prev->next = pos->next;
	free(pos);*/
}

11.判断链表是否为空

bool ListEmpty(LTNode* phead)//判断是否为空,如果是空,返回true
{
	assert(phead);
	return phead->next == phead;
}

12.计算链表中有效值的个数

size_t ListSize(LTNode* phead)//计算链表中有效值的个数
{
	assert(phead);
	size_t size = 0;
	LTNode* tail = phead->next;
	while (tail != phead)
	{
		size++;
		tail = tail->next;
	}
	return size;
}

13.销毁链表

void ListDestroy(LTNode* phead)//销毁链表 
{
	assert(phead);
	LTNode* tail = phead->next;
	while (tail != phead)
	{
		LTNode* nextnode = tail->next;
		free(tail);
		tail = nextnode;
	}
	free(phead);
}

销毁链表时要注意要保证每个结点都释放,同时最后也要将头结点释放free(phead)

三、测试代码

#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <assert.h>
typedef int ListDataType;
typedef struct ListNode {
	ListDataType val;
	struct ListNode* prev;
	struct ListNode* next;
}LTNode;
LTNode* BuyListNode(ListDataType x)
{
	LTNode* newnode = (LTNode*)malloc(sizeof(LTNode));
	if (newnode == NULL)
	{
		perror("malloc");
		exit(-1);
	}
	newnode->val = x;
	newnode->prev = NULL;
	newnode->next = NULL;
	return newnode;
}
void ListInit(LTNode** pphead)//初始化
{
	*pphead = (LTNode*)malloc(sizeof(LTNode));
	*pphead = BuyListNode(-1);
	(*pphead)->next = *pphead;
	(*pphead)->prev = *pphead;
}
//LTNode* ListInit()//初始化
//{
//	LTNode* phead = BuyListNode(-1);//初始化时将头结点置为-1;
//	phead->next = phead;
//	phead->prev = phead;
//	return phead;
//}

void ListPrint(LTNode* phead)//打印
{
	assert(phead);
	LTNode* cur = phead->next;
	while (cur != phead)
	{
		printf("%d ", cur->val);
		cur = cur->next;
	}
	printf("\n");
}
void ListPushBack(LTNode* phead, ListDataType x)//尾插
{
	assert(phead);
	LTNode* newnode = BuyListNode(x);
	LTNode* tail = phead->prev;
	newnode->next = tail->next;
	phead->prev = newnode;
	newnode->prev = tail;
	tail->next = newnode;
	//ListInsert(phead, x);
}
void ListPopBack(LTNode* phead)//尾删
{
	assert(phead);
	assert(phead->next != phead);
	LTNode* prevnode = phead->prev;
	prevnode->prev->next = phead;
	phead->prev = prevnode->prev;
	free(prevnode);
	//ListErase(phead->prev);
}
void ListPushFront(LTNode* phead, ListDataType x)//头插
{
	assert(phead);
	LTNode* tail = phead->next;
	LTNode* newnode = BuyListNode(x);
	tail->prev = newnode;
	newnode->next = tail;
	newnode->prev = phead;
	phead->next = newnode;
	//ListInsert(phead->next,x);
}
void ListPopFront(LTNode* phead)//头删
{
	assert(phead);
	assert(phead->next != phead);
	LTNode* tail = phead->next;
	phead->next = tail->next;
	tail->next->prev = phead;
	//ListErase(phead->next);
}
LTNode* ListFind(LTNode* phead, ListDataType x)//找某个数返回其指针
{
	assert(phead);
	LTNode* cur = phead->next;
	while (cur != phead)
	{
		if (cur->val == x)
		{
			return cur;
		}
		cur = cur->next;
	}
	return NULL;
}
void ListInsert(LTNode* pos, ListDataType x)//在pos之前插入
{
	assert(pos);
	LTNode* newnode = BuyListNode(x);
	LTNode* tail = pos->prev;
	tail->next = newnode;
	newnode->prev = tail;
	newnode->next = pos;
	pos->prev = newnode;
}
void ListErase(LTNode* pos)//删除pos位置
{
	assert(pos);
	LTNode* prevnode = pos->prev;
	LTNode* nextnode = pos->next;
	free(pos);
	prevnode->next = nextnode;
	nextnode->prev = prevnode;
	/*pos->next->prev = pos->prev;
	pos->prev->next = pos->next;
	free(pos);*/
}
bool ListEmpty(LTNode* phead)//判断是否为空,如果是空,返回true
{
	assert(phead);
	return phead->next == phead;
}
size_t ListSize(LTNode* phead)//计算链表中有效值的个数
{
	assert(phead);
	size_t size = 0;
	LTNode* tail = phead->next;
	while (tail != phead)
	{
		size++;
		tail = tail->next;
	}
	return size;
}
void ListDestroy(LTNode* phead)//销毁链表 
{
	assert(phead);
	LTNode* tail = phead->next;
	while (tail != phead)
	{
		LTNode* nextnode = tail->next;
		free(tail);
		tail = nextnode;
	}
	free(phead);
}
void TestList()
{
	//LTNode* plist = ListInit(&plist);
	LTNode* plist = NULL;
	ListInit(&plist);
	ListPushBack(plist, 100);
	ListPushBack(plist, 200);
	ListPushBack(plist, 300);
	ListPushBack(plist, 400);
	ListPushBack(plist, 500);
	ListPopBack(plist);
	ListPopBack(plist);
	ListPopBack(plist);
	ListPrint(plist);
	ListPushFront(plist, 1000);
	ListPushFront(plist, 2000);
	ListPushFront(plist, 3000);
	ListPopFront(plist);
	ListPopFront(plist);
	ListPrint(plist);
	LTNode* pos = ListFind(plist, 1000);
	if (pos != NULL)
	{
		ListInsert(pos, 500);
		ListErase(pos);
	}
	ListPrint(plist);
	if (!ListEmpty(plist))
		printf("%d\n", ListSize(plist));
}
int main()
{
	TestList();
	return 0;
}

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

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

相关文章

峰会实录 | StarRocks PMC Chair 赵纯:数据分析的极速统一3.0 时代

作者&#xff1a;StarRocks PMC Chair 赵纯&#xff08;本文为作者在 StarRocks Summit Asia 2022 上的分享&#xff09; 一年前&#xff0c;StarRocks 源码开放&#xff0c;StarRocks 社区也正式成立。经过一年发展&#xff0c;社区已经获得了 3400 个 Star&#xff0c;7500 …

如何用windows上架ios到苹果商城

1.苹果账号 1.你需要申请苹果账号 官网有提示&#xff1a;Sign In - Apple 2.登录 登录后店家 account&#xff0c;进入account。 点击证书&#xff0c;进入。 2.开始上架步骤 1.注册标识符&#xff08;Bundle ID&#xff09; 进入这个界面后&#xff0c;点击 Identifiers …

Elasticsearch快照备份

目录 1、Repositories 1、配置路径 2、注册快照存储库 2、查看注册的库 3、创建快照 1、为全部索引创建快照 2、为指定索引创建快照 4、查看备份完成的列表 5、删除快照 6、从快照恢复 1、恢复指定索引 2、恢复所有索引&#xff08;除.开头的系统索引&#xff09; …

【Redis】 数据结构:Redis对象与编码(底层结构)对应关系详解

【Redis】 数据结构&#xff1a;Redis对象与编码(底层结构)对应关系详解 文章目录【Redis】 数据结构&#xff1a;Redis对象与编码(底层结构)对应关系详解Redis对象与编码(底层结构)对应关系引入Redis数据结构-RedisObjectredisObject数据结构Redis的编码方式五种数据结构Redis…

2022年深信服杯四川省大学生信息安全技术大赛-CTF-Reverse复现(部分)

Rush B 开始先设置一下数字以16进制格式显示 看主函数 __int64 __fastcall main(int a1, char **a2, char **a3) {int v3; // eaxsize_t v4; // raxint v5; // ecxchar v6; // alint v7; // ecxint v9; // [rsp3Ch] [rbp-404h]char s[1000]; // [rsp40h] [rbp-400h] BYREFchar …

免杀技术(详细)

恶意软件 ● 病毒、木马、蠕虫、键盘记录、僵尸程序、流氓软件、勒索软件、广告程序 ● 在用户非资源的情况下执行安装 ● 出于某种恶意的目的&#xff1a;控制、窃取、勒索、偷窥、推送、攻击。。。。。 恶意程序最重要的防护手段 ● 杀毒软件 / 防病毒软件 ● 客户端 / 服…

c# .net MAUI基础篇 环境安装、新建项目、安卓模拟器安装、项目运行

c# .net MAUI基础篇 环境安装、新建项目、安卓模拟器安装、项目运行 免费教学视频地址由趣编程ACE老师提供&#xff1a; 1..NET MAUI优势及安装和创建_哔哩哔哩_bilibili 一、介绍 .NET 多平台应用 UI (.NET MAUI) 是一个跨平台框架&#xff0c;用于使用 C# 和 XAML 创建本机移…

【面经】之小鼠喝药问题

题目 现在有 10 只小白鼠和 1000 支药水&#xff0c;1000 支药水中有且仅有一支药水有毒&#xff0c;如果小白鼠喝下毒药&#xff0c;那么毒发的时间是两小时。 现在只给你两小时的时间&#xff0c;请问如何用这 10 只小白鼠测出哪支药水有毒&#xff1f;&#xff08;忽略小白…

【Java编程进阶】标识符和关键字

在学习Java程序设计基础的时候&#xff0c;主要有标识符&#xff0c;变量&#xff0c;数据类型&#xff0c;流程控制这些主要的内容。 推荐学习专栏&#xff1a;Java 编程进阶之路【从入门到精通】 文章目录1. 标识符2. 关键字1. 标识符 什么是标识符&#xff1f; 标识符是用…

linux下的PPPOE设置

1.打开终端 #sudo pppoeconf 进入配置,输入用户名和密码. 2.建立连接 #sudo pon dsl-provider 3.断开连接 #sudo poff dsl-provider Welcome to the ADSL client setup. First, I will run some checks on your system to make sure the PPPoE client is installed properly.…

The 2022 CCPC Guangzhou Onsite M. XOR Sum(数位dp 数位背包)

题目 给定n,m,k(0<n<1e15,0<m<1e12,1<k<18)&#xff0c; 求长度为k的数组a&#xff0c;ai为[0,m]的整数&#xff0c; 满足的方案数 答案对1e97取模 题解 第一反应想起了hdu3693&#xff0c;但比对了一下&#xff0c;感觉那个题难很多&#xff0c; 两年…

一看就会的Java方法

文章目录一、方法的定义和使用&#x1f351;1、为什么引入方法&#xff1f;&#x1f351;2、方法的定义&#x1f351;3、方法调用的执行过程&#x1f351;4、实参和形参的关系二、方法重载&#x1f351;1、为什么需要方法重载&#x1f351;2、方法重载的概念和特点&#x1f351…

四旋翼无人机学习第8节--OpenMV电路分析

这里写目录标题0 前言1 openmv优秀作品介绍2 stm32单片机原理图绘制3 stm32单片机外接电容分析3 stm32单片机外接电容绘制4 stm32单片机外接晶振分析5 stm32单片机外接晶振绘制6 stm32单片机复位电路分析7 stm32单片机复位电路设计0 前言 简单的说一下&#xff0c;openmv模块是…

微信小程序 | 吐血整理的日历及日程时间管理

&#x1f4cc;个人主页&#xff1a;个人主页 ​&#x1f9c0; 推荐专栏&#xff1a;小程序开发成神之路 --【这是一个为想要入门和进阶小程序开发专门开启的精品专栏&#xff01;从个人到商业的全套开发教程&#xff0c;实打实的干货分享&#xff0c;确定不来看看&#xff1f; …

关于宝宝过敏原检测的这几点,专家达成共识啦

随着传染病发病率的下降&#xff0c;儿童过敏性疾病的发病率逐年上升&#xff0c;引起了公众和医务人员的广泛关注。四川省妇幼保健医院检验科目前可进行过敏原检测。根据超敏反应的发生机制和临床特点&#xff0c;可分为四种类型。我们所谓的过敏原检查是特异性的IgE相关的Ⅰ超…

React源码之Fiber架构

对于Fiber我们可以理解为存储在内存中的Dom 对于React15在render阶段的reconcile是不可打断的&#xff0c;如果在操作大量的dom时&#xff0c;会存在卡顿&#xff0c;因为浏览器将所有的时间都交给了js引擎线程去执行&#xff0c;此时GUI渲染线程被阻塞&#xff0c;导致页面出现…

PyTorch搭建循环神经网络(RNN)进行文本分类、预测及损失分析(对不同国家的语言单词和姓氏进行分类,附源码和数据集)

需要源码和数据集请点赞关注收藏后评论区留言~~~ 下面我们将使用循环神经网络训练来自18种起源于不同语言的数千种姓氏&#xff0c;并根据拼写方式预测名称的来源。 一、数据准备和预处理 总共有18个txt文件&#xff0c;并且对它们进行预处理&#xff0c;输出如下 部分预处理…

Windows版Ros环境的搭建以及Rviz显示激光点云信息

安装步骤&#xff1a; 1.安装visual studio 2019-2022 2.安装ROS 3.创建ROS快捷终端 4.运行测试效果 一、安装Visual Studio 2022 需要利用vs编译ROS代码&#xff0c;所以需要安装Visual Studio 2022 这里注意要使用vs2022&#xff0c;ROS wiki给的教程是使用2019 1).使…

Python学习小组课程-课程大纲与Python开发环境安装

一、前言 注意&#xff1a;此为内部小组学习资料&#xff0c;非售卖品&#xff0c;仅供学习参考。 为提升项目落地的逻辑思维能力&#xff0c;以及通过自我创造工具来提升工作效率&#xff0c;特成立Python学习小组。计划每周花一个小时进行在线会议直播学习&#xff0c;面向…

力扣21 - 合并两个有序链表【归并排序思维】

链式铠甲——合体一、题目描述二、思路分析三、代码详解way1【不带头结点】way2【带头结点】四、整体代码展示【需要自取】方法一&#xff1a;不带哨兵位【无头结点】方法二&#xff1a;带哨兵位【有头结点】五、总结与提炼一、题目描述 原题传送门&#x1f6aa; 将两个升序链…