链表之双向链表的实现

铁汁们大家好,我们上一篇博客学习了单链表,这节课让我们继续往深学习,学习一下双线链表,话不多说,我们开始吧!



目录

1.双向链表

2.顺序表和链表的优缺点

3.双向链表的实现


1.双向链表

1.我们要实现的双线链表是带头双向循环链表,它的结构最复杂,一般用在单独的存储数据。我们实际中使用的链表数据结构都是带头双向循环链表。

2.它虽然结构复杂,但是在我们用代码实现过程中,它比单链表简单。

3.相信很多铁汁不清楚双向链表的结构是什么,如下图:

2.顺序表和链表的优缺点

我们在这里总结一下这两种线性表,方便之后的学习。

顺序表:

优点:空间连续,支持随机访问

缺点:中间或前面部分的插入和删除,时间复杂度是O(n);

           增容很不方便,代价较大。

链表:

优点:任意位置的插入删除,时间复杂度为O(1);

           没有增容销耗,按需申请节点空间,不用了直接释放。

缺点:以节点为单位存储,不支持随机访问

3.双向链表的实现

经过上面的铺垫,我们来实现一个带头双向循环链表

List.h文件

#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
#include<stdbool.h>

typedef int ADataType;
typedef struct ListNode
{
	ADataType data;
	struct ListNode* prev;//双线链表前驱指针
	struct ListNode* next;//后继指针
}LN;

//双向链表初始化
LN* ListInit();
//为节点开辟空间
LN* BuyListCapacity(ADataType x);
//链表的销毁
void ListDestory(LN* phead);
//头插
void ListPushFront(LN* phead, ADataType x);
//尾插
void ListPushBack(LN* phead, ADataType x);
//打印
void ListPrint(LN* phead);
//头删
void ListPopFront(LN* phead);
//尾删
void ListPopBack(LN* phead);
//查找链表中的数据
LN* ListSearch(LN* phead, ADataType x);
//修改找到的数据
void ListModify( LN* pos, ADataType y);
//在这个位置后插入数据
void ListInsert(LN* pos, ADataType x);
//删除这个位置之后的数据
void ListErase(LN* pos);
//判断链表是否为空
bool ListEmpty(LN* phead);

List.c文件

#include"List.h"

//为节点开辟空间
LN* BuyListCapacity(ADataType x)
{
	LN* newnode = (LN*)malloc(sizeof(LN));
	if (newnode == NULL)
	{
		perror("malloc is false!\n");
		exit(-1);
	}
	newnode->data = x;
	newnode->next = NULL;
	newnode->prev = NULL;
	return newnode;
}

//双向链表初始化
LN* ListInit()
{
	//开辟空间
	LN* phead = BuyListCapacity(0);
	//让头节点的前驱和后继都指向自己,是一个循环
	phead->next = phead;
	phead->prev = phead;
	return phead;
}

//链表的销毁
void ListDestory(LN* phead)
{
	assert(phead);
	LN* tail = phead->next;
	while (tail != phead)//链表的头尾相连,当尾等于头时,说明链表空了
	{
		LN* next = tail->next;
		free(tail);
		tail = next;
	}
	free(phead);
	phead = NULL;
}
//头插
void ListPushFront(LN* phead, ADataType x)
{
	assert(phead);
	LN* newnode = BuyListCapacity(x);
	//若这里不使用新的变量来存储原来第一个节点的值,就先链接后,在链接前
	newnode->next = phead->next;
	phead->next->prev = newnode;
	phead->next = newnode;
	newnode->prev = phead;
}
//尾插
void ListPushBack(LN* phead, ADataType x)
{
	assert(phead);
	LN* newnode = BuyListCapacity(x);
	//找到尾,进行插入节点
	LN* tail = phead->prev;
	tail->next = newnode;
	newnode->prev = tail;
	phead->prev = newnode;
	newnode->next = phead;
}
//打印
void ListPrint(LN* phead)
{
	assert(phead);
	LN* tail = phead->next;
	while (tail != phead)
	{
		printf(" %d ", tail->data);
		tail = tail->next;
	}
	printf("\n");
}
//头删
void ListPopFront(LN* phead)
{
	assert(phead);
	//判断链表是否为空,为空则删不了
	if (phead->next == phead)
	{
		printf("List is NULL!\n");
		return;
	}
	//先记录下后一个节点
	LN* first = phead->next;
	LN* second = first->next;
	phead->next = second;
	second->prev = phead;
	free(first);
	first = NULL;
}
//尾删
void ListPopBack(LN* phead)
{
	assert(phead);
	//判断链表是否为空
	if (phead->prev == phead)
	{
		printf("List is NULL!\n");
		return;
	}
	LN* tail = phead->prev;
	LN* prev = tail->prev;
	prev->next = phead;
	phead->prev = prev;
	free(tail);
	tail = NULL;
}
//查找链表中的数据
LN* ListSearch(LN* phead, ADataType x)
{
	assert(phead);
	LN* cur = phead->next;
	while (cur->data != x)
	{
		cur = cur->next;
	}
	if (cur->data == x)
	{
		return cur;
	}
	return NULL;
}
//修改找到的数据
void ListModify( LN* pos, ADataType y)
{
	assert(pos);
	pos->data = y;
}
//在这个位置后插入数据
void ListInsert(LN* pos, ADataType x)
{
	assert(pos);
	LN* newnode = BuyListCapacity(x);
	LN* next = pos->next;
	pos->next = newnode;
	newnode->prev = pos;
	newnode->next = next;
	next->prev = newnode;
}
//删除这个位置之后的数据
void ListErase(LN* pos)
{
	assert(pos);
	LN* cur = pos->next;
	LN* next = cur->next;
	pos->next = next;
	next->prev = pos;
	free(cur);
	cur = NULL;
}
//判断链表是否为空
bool ListEmpty(LN* phead)
{
	assert(phead);
	if (phead->prev == phead || phead->next == phead)
	{
		return true;
	}
	return false;
}

Test.c文件

#include"List.h"
//带头双向循环链表的实现

void Test1()
{
	LN* head = ListInit();
	ListPushFront(head, 33);
	ListPushFront(head, 22);
	ListPushFront(head, 11);
	ListPushBack(head, 4);
	ListPushBack(head, 5);
	ListPushBack(head, 6);
	ListPushBack(head, 7);
	ListPushBack(head, 8);
	ListPushBack(head, 9);
	ListPushBack(head, 10);
	printf("ListNode:> ");
	ListPrint(head);

	ListPopFront(head);
	ListPopBack(head);
	printf("ListNode:> ");
	ListPrint(head);

	LN* pos = ListSearch(head, 7);
	if (pos == NULL)
	{
		printf("Not Find!\n");
	}
	else
	{
		printf("the number is %d\n", pos->data);
		ListModify(pos, 77);
		printf("ListNode:> ");
		ListPrint(head);

		ListInsert(pos, 13);
		ListInsert(pos, 14);
		ListInsert(pos, 15);
		printf("ListNode:> ");
		ListPrint(head);
		ListErase(pos);
		printf("ListNode:> ");
		ListPrint(head);
	}
	if (ListEmpty(head))
	{
		printf("List is NULL!\n");
	}
	else
	{
		printf("List is Notnull!\n");
	}

	ListDestory(head);
	printf("List is disory!\n");
}

int main()
{
	Test1();
	return 0;
}

结果:结果就是这样的,大家可以自己尝试一下!


这就是双向链表的实现,大家还是要自己敲一遍代码,帮助自己更好的掌握知识点。

谢谢铁汁们的支持,咱们下期再见!!!

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Elasticsearch快速上手

基本概念 索引&#xff08;Index&#xff09; 索引是文档的容器&#xff0c;就像关系数据库中&#xff0c;要存储行记录必须先创建数据库和表一样。 类型&#xff08;Type&#xff09; ES6 及之前的版本还存在”类型“的概念&#xff0c;一个索引下可以存储多个类型的文档&am…

探索数据结构:特殊的双向队列

✨✨ 欢迎大家来到贝蒂大讲堂✨✨ &#x1f388;&#x1f388;养成好习惯&#xff0c;先赞后看哦~&#x1f388;&#x1f388; 所属专栏&#xff1a;数据结构与算法 贝蒂的主页&#xff1a;Betty’s blog 1. 双向队列的定义 **双向队列(double‑ended queue)**是一种特殊的队列…

Ant Design Pro | 前端项目初始化

初始化项目 环境确认 这里使用的版本如下&#xff1a; 新建文件夹&#xff08;fapi&#xff09; 执行项目初始化命令 cmd进入命令行执行项目初始化命令&#xff0c;参考官网https://pro.ant.design/zh-CN/ # 使用 npm npm i ant-design/pro-cli -g # fapi-frontend是项目…

树莓派固件烧录教程(2024)

一、烧录工具准备 硬件准备&#xff1a; 16G及以上TF卡和读卡器&#xff0c;TF卡建议高速卡&#xff08;卡的读写速度直接影响树莓派的运行速度&#xff09;。 软件准备&#xff1a;&#xff08;下面二方法选其一即可&#xff09; 方法1&#xff1a;raspberry官方烧录工具R…

【高校科研前沿】中国科学院南京地理与湖泊研究所肖启涛博士为一作在Sci. Bull发文:我国湖泊二氧化碳从大气的源向汇转变

目录 1.文章简介 2.研究内容 3.文章引用 1.文章简介 论文名称&#xff1a;Lakes shifted from a carbon dioxide source to a sink over past two decades in China 第一作者及通讯作者&#xff1a;肖启涛&#xff08;博士生&#xff09;&#xff0c;段洪涛&#xff08;研究…

JavaSE继承和多态(下)

在了解多态之前我们先弄清以下三个概念&#xff1a; 方法的重写向上转型和向下转型动态绑定和静态绑定 一.方法的重写 重写(override)&#xff1a;也称为覆盖。重写是子类对父类非静态、非private修饰&#xff0c;非final修饰&#xff0c;非构造方法等的实现过程 进行重新编写,…

如何使用 langchain 与 openAI 连接

上一篇写了如何安装 langchain https://www.cnblogs.com/hailexuexi/p/18087602 这里主要说一个 langchain的使用 创建一个目录 langchain &#xff0c;在这个目录下创建两个文件 main.py 这段python代码&#xff0c;用到了openAI&#xff0c;需要openAI及FQ。这里只做…

c++的学习之路:16、string(3)

上章有一些东西当时没学到&#xff0c;这里学到了将在补充&#xff0c;文章末附上代码&#xff0c;思维导图。 目录 一、赋值重载 二、带模板的创建 三、析构函数 四、代码 五、思维导图 一、赋值重载 这里的赋值重载就是直接利用交换函数进行把传参生成的临时数据和需要…

IDEA中的Debug功能介绍

说明&#xff1a;本文介绍IDEA中的Debug功能&#xff0c;基于2023.2&#xff08;Ultimate Edition&#xff09;版本 简单介绍 首先&#xff0c;在程序需要停止的所在行号上&#xff0c;鼠标左键&#xff0c;可设置一个断点&#xff0c;是一个红色圆点标志&#xff0c;表示程序…

2023年下半年中级软件设计师上午真题及答案解析

01 02 03 04 05 06 07 08 09 10 篇幅有限&#xff0c;私我获取免费完整 pdf文件

php反序列化题目

[NewStarCTF 公开赛赛道]UnserializeOne 分析代码&#xff0c;最终需要调用到 file_get_contents 即可获得flag 从后往前分析 触发 __invoke 需要 以调用函数的方式调用一个对象 可以找到Start类 里的__isset中可以将类当作函数调用 所以需要调用到 __isset 就需要 isset()…

Steam上线真人乙游,女性玩家还愿意买单吗?

Steam上线了一款真人乙游《糟糕&#xff01;他们太爱我了怎么办&#xff1f;》&#xff08;以下简称《糟糕&#xff01;&#xff09;。 乍一听这个游戏名&#xff0c;似乎和《完蛋&#xff01;我被美女包围了&#xff01;》有异曲同工之妙&#xff0c;事实也确实如此&#xff…

实现通讯录(顺序表版本)

一、功能要求 &#xff08;1&#xff09;⾄少能够存储100个⼈的通讯信息 &#xff08;2&#xff09;能够保存⽤⼾信息&#xff1a;名字、性别、年龄、电话、地址等 &#xff08;3&#xff09;增加联系⼈信息 &#xff08;4&#xff09;删除指定联系⼈ &#xff08;5&#…

国内:深圳交通流量数据集

数据来源&#xff1a;深圳政府数据开放平台&#xff08;深圳市政府数据开放平台&#xff09;&#xff0c;这个官网上还有其他类数据集&#xff0c;值得收藏&#xff01;&#xff01;&#xff01; 数据集介绍&#xff1a;宝安区-G4高速西乡大道入口车流量统计 第一行每列的标题…

什么是超导悬浮?工作原理是什么?

某些材料在冷却到某个温度&#xff08;也称为“临界温度”&#xff09;以下时会完全失去电阻。 1910 年&#xff0c;一位名叫 Heike Kamerlingh Onnes 的荷兰物理学家发现了这一现象。他注意到低于一定温度时电阻突然下降&#xff0c;然后他大胆地声称发现了一种新的物质状态&a…

字符串处理

读取 先定义&#xff1a; char ch[100];string s; cin>>s或cin>>ch以空格或换行符结束gets(ch);//gets只能读字符数组&#xff0c;不能直接读字符串stringgets和getline会把第一次出现的换行符及先前的字符串读进去&#xff08;包括空格&#xff09;&#xff0…

利用Flutter框架实现iOS应用的跨平台发布策略

本文探讨了使用Flutter开发的iOS应用能否上架&#xff0c;以及上架的具体流程。苹果提供了App Store作为正式上架渠道&#xff0c;同时也有TestFlight供开发者进行内测。合规并通过审核后&#xff0c;Flutter应用可以顺利上架。但上架过程可能存在一些挑战&#xff0c;因此可能…

【Linux】正则表达式实验操作实例

正则表达式是一种强大的工具&#xff0c;用于在文本中查找、匹配和替换特定的字符串模式。 实验目的 掌握正则表达式的表达方式掌握grep/egrep命令的用法掌握sed 命令的用法掌握awk命令的用法 正则表达式 实验目的实验内容实验过程创建grep文件来进行如下操作用sed命令完成下列…

HAL STM32 定时器PWM DMA输出方式

HAL STM32 定时器PWM DMA输出方式 &#x1f9e8;遗留问题&#xff1a;当配置RCR重复计数器&#xff0c;配置为2时&#xff0c;在定义了3组PWM参数情况下&#xff0c;只能输出第二组参数的PWM波形。&#xff08;HAL_TIM_PWM_Start_DMA(&htim1, TIM_CHANNEL_1, aCCValue_Buff…

Java中网络编程,Junit单元测试详解

文章目录 软件结构C/S结构B/S结构 概述三要素IP &#xff08;银行的位置&#xff09;端口 (银行中某个柜台号)协议 (填写取款单的规则)TCP通信程序TCP通信原理客户端发送数据服务端接收数据过程图三次握手 Junit单元测试概述常见的注解使用断言概述使用 软件结构 C/S结构 客户…