数据结构——双向链表

这篇具有很好参考价值的文章主要介绍了数据结构——双向链表。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

数据结构双向表,数据结构,链表,数据结构

🍇系列专栏:🌙数据结构

🍉 欢迎关注:👍点赞🍃收藏🔥留言

🍎 博客主页:🌙_麦麦_的博客_CSDN博客-领域博主
🌙如果我们都不能够拥有黑夜,又该怎样去仰望星空? 

数据结构双向表,数据结构,链表,数据结构

目录

一、前言

二、正文——双向链表的实现

2.1模块化

2.2 数据类型与结构体定义

 2.3链表的初始化

 2.4链表的打印

2.5 链表的查找

 2.6判断链表是否只有哨兵卫

2.7申请新的结点 

2.8 链表的尾插

 2.9链表的尾删

 2.10链表的头插

2.11链表的头删

2.12链表的插入【pos之前】

2.13链表的删除 

2.14链表的销毁

 三、双向链表完整代码(含测试模块)

3.1 List.h

3.2 List.c

3.3 text.c

四、结语

一、前言

        在笔者的前两篇文章具体讲解的单链表的实现,即无头单向非循环。但是除此之外,链表还有另外七种类型,本篇文章将主要介绍双向链表的实现,即带头双向链表。相信有的小伙伴一定会问那另外六种需不需要讲解呢,要不要掌握呢?其实,条条大路通罗马,在掌握的单链表和双链表这两大基础链表,其他的链表的实现和运用也是轻而易举,毫不费力

        因此,就让我们一起实现双向链表叭!

       数据结构双向表,数据结构,链表,数据结构

二、正文——双向链表的实现

数据结构双向表,数据结构,链表,数据结构

2.1模块化

         相信看过博主前几篇文章的小伙伴对“模块化”已经熟悉的不能再熟悉了,为了后期代码的修改和维护能够更加简便,我们这次双向链表的实现依旧采取模块化的方式。本次的程序共分为3个文件,分别是:List.c[具体功能的实现]、List.h[[函数、变量的声明]]、Text.c[测试部分]。

2.2 数据类型与结构体定义

        在进行链表功能的实现之前,首先是要对数据类型进行定义结构体定义

        数据类型采取的是整形的形式,与前文的单链表相同。

        结构体定义这一块则与单链表的定义有所不同。由于单链表是单向链表,故每个结点只需存放下一个结点的地址,而无需存放上一个结点的地址。而双向链表,顾名思义是双向的,通过一个结点我们能够找到它的上一个结点和下一个结点。因此在对结构体进行定义的时候我们就需要两个结构体指针,分别用于存放该结点的上下结点。

        具体代码实现如下:

//数据类型与结构体定义
typedef int LTDataType;
typedef struct ListNode
{
	struct ListNode* next;    //存放下一个结点
	struct ListNode* prev;    //存放上一个结点
	LTDataType data;          //数据类型  
}LTNode;

 2.3链表的初始化

        在进行完数据类型和结构体定义,还需要做一些准备工作,来帮助我们更好的实现和测试后续的链表功能。而准备工作大致分为以下几点:链表的初始化链表的打印链表的查找判断链表是否只有哨兵卫申请新的结点

        我们先从链表的初始化开始吧。首先毫无疑问的是先建立的链表的哨兵卫,为了表现链表双向的特性,接下来我们还要将哨兵卫的上下结点指向它本身

        具体代码如下:

//链表初始化
LTNode* LTInit();

//链表初始化
LTNode* LTInit()
{
	LTNode* phead =BuyListNode(-1);
	phead->next = phead;    //下一个结点指向本身
	phead->prev = phead;    //上一个结点指向本身
	return phead;
}

 2.4链表的打印

        链表的打印是为了更好的测试我们后续的插入、删除是否完成。那么该如何实现链表的打印呢?

        双向链表的打印与单链表也是有所不同的。对于单链表的打印,我们只需从从头指针开始遍历链表,直至为空。而对于双向链表来说,遍历链表是不可行的,这是由它的结构决定的。双向链表的链接是循环的,单凭遍历的话,是永远也不可能将这个链表打印结束的,因此我们就需要做一点小小的改变,让遍历链表的指针【cur】从哨兵卫的下一个结点开始遍历,直至遍历到哨兵卫停止,这样就可以将链表的数据全部打印出来了。

        具体代码如下:

//链表的打印[声明]
void LTPrint(LTNode* phead);

//链表的打印[具体实现]
void LTPrint(LTNode* phead)
{
	assert(phead);
	LTNode* cur = phead->next;
	while (cur != phead)
	{
		if (cur->next == phead)
		{
			printf("<->%d<->", cur->data);
		}
		else
		{
			printf("<->%d", cur->data);
		}
		cur = cur->next;
	}
	printf("\n");
}

2.5 链表的查找

        为了后续链表指定位置的插入和删除,我们需要找到指定数据所指向的结点,为了提高代码的复用,我们将链表的查找单独封装成一个函数

        那么该如何去编写呢?这里的代码其实与单向链表差不多,即遍历链表,直至找到数据所对应的结点,并将其地址返回即可。相对而言还是很简单的。

        具体代码实现如下:

//链表的查找[声明]
LTNode* LTFind(LTNode* phead, LTDataType x);

//链表的查找[实现]
LTNode* LTFind(LTNode* phead, LTDataType x)
{
	assert(phead);
	LTNode* cur = phead->next;
	while (cur != phead)
	{
		if (cur->data == x)
		{
			return cur;
		}
		cur = cur->next;
	}
	return NULL;
}

 2.6判断链表是否只有哨兵卫

        这一部分是为了链表的删除的准备的,对于单链表而言,当链表为空的时候,就不能进行数据的删除了,而对于双向链表来说,有了哨兵卫的存在,自然就不存在链表为空的情况,相应的,就是当双向链表只存在哨兵卫的时候,就不能进行数据的删除了。

        那么如何判断链表是否只有哨兵卫呢?方法其实很简单,只需要判断所给的头指针的上下结点是否指向本身即可,当然两条件选其一即可。

        具体代码实现如下:

//判断链表是否只有哨兵卫[声明]
bool LTEmpty(LTNode* phead);

//判断链表是否只有哨兵卫[实现]
bool LTEmpty(LTNode* phead)
{
	assert(phead);
	return phead->next == phead;
}

2.7申请新的结点 

        这一部分主要是为了链表的插入而准备的,无论是头插、尾插还是指定位置的插入都需要申请新的结点,而重复的书写未免使代码显得冗杂,因此将“申请新的结点”这一功能单独分离出来并封装成一个函数,大大提高代码的简洁性。

        而这一功能的实现与单链表大同小异,都是使用内存函数来向内存申请一片空间并返回指向这片空间的指针,不同的只是结构体的内容发生了改变而已。

        具体代码如下:

//申请新的结点[声明]
LTNode* BuyListNode(LTDataType x);

/申请新的结点[实现]
LTNode* BuyListNode(LTDataType x)
{
	LTNode* newnode = malloc(sizeof(LTNode));
	if (newnode == NULL)
	{
		perror("malloc error");
	}
	else
	{
		newnode->data = x;
		//避免野指针
		newnode->next = NULL;
		newnode->prev = NULL;
		return newnode;
	}
}

2.8 链表的尾插

        准备工作准备就绪后,就开始链表的主要功能的实现啦。

        首先就是链表的尾插。我们先来看看单链表是如何进行尾插的,对于单链表,我们需要对单链表进行遍历找到最后一个结点,再对这个结点和新结点进行链接的操作。而对于双向链表,就没有这么复杂了,因为哨兵卫的上一个结点就已经指向了链表的最后一个结点,在找到这个尾结点之后,剩下的就是链接的问题了。相比于单链表要复杂一点,要将哨兵卫、尾结点、新结点这三个结点进行链接

        具体链接和代码如下:

数据结构双向表,数据结构,链表,数据结构

//链表的尾插[声明]
void LTPushBack(LTNode* phead, LTDataType x);

//链表的尾插[实现]
void LTPushBack(LTNode* phead, LTDataType x)
{
	assert(phead);
	LTNode* tail = phead->prev;
	//创建新的结点
	LTNode* newnode = BuyListNode(x);
	//尾结点的链接
	tail->next = newnode;
	newnode->prev = tail;
	//哨兵卫的链接
	phead->prev = newnode;
	newnode->next = phead;
}

 2.9链表的尾删

         在讲解完链表的尾插,接着就是链表的尾删了。同样地,我们不需要对链表进行遍历来找到尾结点,只需要通过哨兵卫来找到最后一个结点,并将其置空,再将倒数第二个结点和哨兵卫进行链接。不

        具体图片和代码如下:

数据结构双向表,数据结构,链表,数据结构

//链表的尾删
void LTPopBack(LTNode* phead);

//链表的尾删
void LTPopBack(LTNode* phead)
{
	assert(phead);
	//判断是否只有哨兵卫
	assert(!LTEmpty(phead));
	//改变哨兵卫的链接
	phead->prev = phead->prev->prev;
	//释放尾结点的内存
	free(phead->prev->next);
	//改变尾部链接
	phead->prev->next = phead;
}

 2.10链表的头插

        在尾插、尾删之后,就是头插、头删了,那么该如何实现链表的头插呢。

        其步骤也很简单,先是申请一个新的结点,进而将该结点与哨兵卫与链表的头结点进行链接。需要注意的一点就是判断链表是否只有哨兵卫,若是只有哨兵卫就无须删除了。 

        具体图片和代码如下:

数据结构双向表,数据结构,链表,数据结构

//链表的头插[声明]
void LTPushFront(LTNode* phead, LTDataType x);

//链表的头插[实现]
void LTPushFront(LTNode* phead, LTDataType x)
{
	//创建新的结点
	LTNode* newnode = BuyListNode(x);
	LTNode* first = phead->next;
	//改变链接关系
	newnode->next = first;
	first->prev = newnode;
	phead->next = newnode;
	newnode->prev = phead;
	//代码复用
	//LTInsert(phead->next, x);
}

2.11链表的头删

        头插之后便是头删了,这一部分的代码也是相对简单的,主要思路是将哨兵卫与头结点的下一个结点链接,并将头结点的内存释放。唯一需要注意的一点就是判断链表是否只有哨兵卫,若是只有哨兵卫就无须删除了。 

数据结构双向表,数据结构,链表,数据结构

//链表的头删[声明]
void LTPopFront(LTNode* phead);

//链表的头删[实现]
void LTPopFront(LTNode* phead)
{
	assert(phead);
	//改变链接关系+释放内存
	phead->next = phead->next->next;
	free(phead->next->prev);
	phead->next->prev = phead;
}

2.12链表的插入【pos之前】

        链表的头插、头删、尾插、尾删已经全部讲解完毕了,但其实对于双向链表来说,以上四个功能并不需要,只需要通过链表的插入和删除便可以实现,也就是代码的复用。但是为了帮助小伙伴们更好的理解双向链表,便将上述功能一一实现。

        接下来就让我们进入“”链表的插入“”这一功能的实现,我们采取的是在"pos"之前进行数据的插入。对“pos”之后插入数据的感兴趣的小伙伴也可以尝试自己编写,两者的逻辑是类似的。

        具体的实现逻辑呢,就是申请一个新的结点,并将pos位置的结点、pos位置之前的结点和新结点进行链接。不过需要注意的一点是,如果你用的的指针过少,便要注意链接的顺序,若是链接顺序便可能无法找到原来结点的数据,比较保险且快捷的方法就是再另外设置一个指针用来记录链接的结点,便可以毫无顾虑的进行结点间的链接了。

注:在尾插的代码中对该段代码的复用有些独特,是通过传哨兵卫的指针进行插入,这是由于双向链表的循环特性决定的,链表的最后一个结点就是哨兵卫的前一个结点

数据结构双向表,数据结构,链表,数据结构

//链表的插入(前)[声明]
void LTInsert(LTNode* pos, LTDataType x);

//链表的插入(前)[实现]
void LTInsert(LTNode* pos, LTDataType x)
{
	assert(pos);
	LTNode* newnode = BuyListNode(x);
	LTNode* prev = pos->prev;
	//改变链接关系
	prev->next = newnode;
	newnode->prev = prev;
	newnode->next = pos;
	pos->prev = newnode;
}

2.13链表的删除 

         链表的删除的实现逻辑大致就是将要删除的结点置空,并将其前后两个结点进行链接即可。不过要注意的依旧是链表是否只含有哨兵卫,若只含有哨兵卫,就无需进行链表的删除。

数据结构双向表,数据结构,链表,数据结构

//链表的删除[声明]
void LTErase(LTNode* pos);

//链表的删除[实现]
void LTErase(LTNode* pos)
{
	assert(pos);
	LTNode* next = pos->next;
	LTNode* prev = pos->prev;
	//改变链接关系并释放内存
	prev->next = next;
	next->prev = prev;
	free(pos);
}

2.14链表的销毁

        不知不觉之中,就来到了双向链表的最后一个功能:链表的销毁

        其实现逻辑:从哨兵卫的下一个结点开始遍历链表,边遍历边销毁,直至遍历到哨兵卫为止,最后将哨兵卫的内存销毁并将指针置空

//链表的销毁[声明]
void LTDestroy(LTNode* phead);

//链表的销毁[实现]
void LTDestroy(LTNode* phead)
{
	assert(phead);
	LTNode* cur = phead->next;
	while (cur != phead)
	{
		LTNode* next = cur->next;
		free(cur);
		cur = next;
	}
	free(phead);
	phead = cur=NULL;
}

 三、双向链表完整代码(含测试模块)

3.1 List.h

#include<stdlib.h>
#include<assert.h>
#include<stdio.h>
#include<stdbool.h>

//数据类型与结构体定义
typedef int LTDataType;
typedef struct ListNode
{
	struct ListNode* next;	//存放下一个结点
	struct ListNode* prev;	//存放上一个结点
	LTDataType data;		//数据类型 
}LTNode;

//申请新的结点
LTNode* BuyListNode(LTDataType x);
//链表初始化
LTNode* LTInit();
//链表的打印
void LTPrint(LTNode* phead);
//判断链表是否只有哨兵卫
bool LTEmpty(LTNode* phead);
//链表的查找
LTNode* LTFind(LTNode* phead, LTDataType x);
//链表的尾插
void LTPushBack(LTNode* phead, LTDataType x);
//链表的尾删
void LTPopBack(LTNode* phead);
//链表的头插
void LTPushFront(LTNode* phead, LTDataType x);
//链表的头删
void LTPopFront(LTNode* phead);
//链表的插入(前)
void LTInsert(LTNode* pos, LTDataType x);
//链表的删除
void LTErase(LTNode* pos);
//链表的销毁
void LTDestroy(LTNode* phead);

3.2 List.c

#include "List.h"


//申请新的结点
LTNode* BuyListNode(LTDataType x)
{
	LTNode* newnode = malloc(sizeof(LTNode));
	if (newnode == NULL)
	{
		perror("malloc error");
	}
	else
	{
		newnode->data = x;
		//避免野指针
		newnode->next = NULL;
		newnode->prev = NULL;
		return newnode;
	}
}
//链表初始化
LTNode* LTInit()
{
	LTNode* phead =BuyListNode(-1);
	phead->next = phead;	//下一个结点指向本身
	phead->prev = phead;	//上一个结点指向本身
	return phead;
}

//判断链表是否只有哨兵卫
bool LTEmpty(LTNode* phead)
{
	assert(phead);
	return phead->next == phead;
}

//链表的查找
LTNode* LTFind(LTNode* phead, LTDataType x)
{
	assert(phead);
	LTNode* cur = phead->next;
	while (cur != phead)
	{
		if (cur->data == x)
		{
			return cur;
		}
		cur = cur->next;
	}
	return NULL;
}


//链表的打印
void LTPrint(LTNode* phead)
{
	assert(phead);
	LTNode* cur = phead->next;
	while (cur != phead)
	{
		if (cur->next == phead)
		{
			printf("<->%d<->", cur->data);
		}
		else
		{
			printf("<->%d", cur->data);
		}
		cur = cur->next;
	}
	printf("\n");
}

//链表的尾插
void LTPushBack(LTNode* phead, LTDataType x)
{
	assert(phead);
	LTNode* tail = phead->prev;
	//创建新的结点
	LTNode* newnode = BuyListNode(x);
	//尾结点的链接
	tail->next = newnode;
	newnode->prev = tail;
	//哨兵卫的链接
	phead->prev = newnode;
	newnode->next = phead;
	//代码复用
	//LTInsert(phead, x);
}



//链表的尾删
void LTPopBack(LTNode* phead)
{
	assert(phead);
	//判断是否只有哨兵卫
	assert(!LTEmpty(phead));
	//改变哨兵卫的链接
	phead->prev = phead->prev->prev;
	//释放尾结点的内存
	free(phead->prev->next);
	//改变尾部链接
	phead->prev->next = phead;
	//代码复用
	//LTErase(phead->prev);
}

//链表的头插
void LTPushFront(LTNode* phead, LTDataType x)
{
	//创建新的结点
	LTNode* newnode = BuyListNode(x);
	LTNode* first = phead->next;
	//改变链接关系
	newnode->next = first;
	first->prev = newnode;
	phead->next = newnode;
	newnode->prev = phead;
	//代码复用
	//LTInsert(phead->next, x);
}

//链表的头删
void LTPopFront(LTNode* phead)
{
	assert(phead);
	//改变链接关系+释放内存
	phead->next = phead->next->next;
	free(phead->next->prev);
	phead->next->prev = phead;
	//代码复用
	//LTErase(phead->next);

}

//链表的插入(前)
void LTInsert(LTNode* pos, LTDataType x)
{
	assert(pos);
	LTNode* newnode = BuyListNode(x);
	LTNode* prev = pos->prev;
	//改变链接关系
	prev->next = newnode;
	newnode->prev = prev;
	newnode->next = pos;
	pos->prev = newnode;
}

//链表的删除
void LTErase(LTNode* pos)
{
	assert(pos);
	LTNode* next = pos->next;
	LTNode* prev = pos->prev;
	//改变链接关系并释放内存
	prev->next = next;
	next->prev = prev;
	free(pos);
}

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

3.3 text.c

#include "List.h"

//测试代码1:尾插
text1()
{
	LTNode* phead = LTInit();
	LTPushBack(phead, 1);
	LTPushBack(phead, 2);
	LTPushBack(phead, 3);
	LTPushBack(phead, 4);
	LTPrint(phead);
}

//测试代码2:尾删
text2()
{
	LTNode* phead = LTInit();
	LTPushBack(phead, 1);
	LTPushBack(phead, 2);
	LTPushBack(phead, 3);
	LTPushBack(phead, 4);
	LTPrint(phead);
	printf("\n");
	LTPopBack(phead);
	LTPopBack(phead);
	LTPrint(phead);
}


//测试代码3:头插
text3()
{
	LTNode* phead = LTInit();
	LTPushFront(phead, 4);
	LTPushFront(phead, 3);
	LTPushFront(phead, 2);
	LTPushFront(phead, 1);
	LTPrint(phead);
}


//测试代码4:头删
text4()
{
	LTNode* phead = LTInit();
	LTPushFront(phead, 4);
	LTPushFront(phead, 3);
	LTPushFront(phead, 2);
	LTPushFront(phead, 1);
	LTPopFront(phead);
	LTPopFront(phead);
	LTPrint(phead);
}


//测试代码5:插入(前)
text5()
{
	LTNode* phead = LTInit();
	LTPushFront(phead, 4);
	LTPushFront(phead, 2);
	LTPushFront(phead, 1);
	LTPrint(phead);
	LTNode* pos=LTFind( phead,4);
	LTInsert(pos, 3);
	LTPrint(phead);
}
//测试代码6:插入(前)
text6()
{
	LTNode* phead = LTInit();
	LTPushFront(phead, 4);
	LTPushFront(phead, 2);
	LTPushFront(phead, 1);
	LTPrint(phead);
	LTNode* pos = LTFind(phead, 4);
	LTInsert(pos, 3);
	LTPrint(phead);
}

//测试代码7:删除
text7()
{
	LTNode* phead = LTInit();
	LTPushFront(phead, 4);
	LTPushFront(phead, 3);
	LTPushFront(phead, 2);
	LTPushFront(phead, 1);
	LTPrint(phead);
	LTNode* pos = LTFind(phead, 3);
	LTErase(pos);
	LTPrint(phead);
}
int main()
{
	//text1();
	//text2();
	//text3();
	//text4();
	//text5();
	//text6();
	text7();
}

四、结语

        至此为止,关于双向链表的讲解已经全部结束,看到这里的小伙伴快给自己点个赞,希望你们能够从本篇文章中有所收获,就是对笔者最大的支持!

        关注我 _麦麦_分享更多干货:_麦麦_的博客_CSDN博客-领域博主
        大家的「关注❤️ + 点赞👍 + 收藏⭐」就是我创作的最大动力!谢谢大家的支持,我们下期见!

 数据结构双向表,数据结构,链表,数据结构 文章来源地址https://www.toymoban.com/news/detail-788324.html

到了这里,关于数据结构——双向链表的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 数据结构---双向链表

    单向链表:一块内存指向下一个内存。 单链表存在一些缺陷: 1.查找速度慢。 2.不能从后往前找。 3.找不到前驱。 链表的结构分为8种: 1.单向和双向 2.带头和不带头 带头的链表有一个带哨兵位的头结点,这个节点不存储有效数据。 好处 :尾插更方便,不需要二级指针了,

    2024年02月02日
    浏览(46)
  • 数据结构-双向链表

    在单链表那一篇博客中介绍了单链表和双向链表的优缺点,所以此篇博客直接分享怎样实现一个带头双向循环链表。 单链表博客: 首先我们需要写一个结构体,双向带头链表的话需要一个前驱指针prev和一个后驱指针next,前驱指针的作用是方便找尾节点,因为头节点的prev指

    2024年02月05日
    浏览(52)
  • 数据结构 - 双向链表

    文章目录 目录 文章目录 前言 一、什么是双向链表? 双向链表有什么优势? 二、双向链表的设计和实现 1.设计思想 尾增 : 在链表的末尾添加新的元素  头插 : 在链表头部插入节点  删除 : 根据val的值删除节点  查找 : 根据索引的值查找并返回节点 总结 大家好,今天给大家讲解

    2024年02月09日
    浏览(39)
  • 数据结构——实现双向链表

    怎么说呢?光乍一听名字好像很难的样子是吧,那如果你这样认为的话,可就要让你大跌眼镜了哦,其实双向带头循环链表从操作和理解上来说都是要易于单项不带头不循环链表(俗称单链表)的。 咱们就来见识见识吧!希望真的能让你们“大跌眼镜”哈! 双向带头循环链

    2024年02月07日
    浏览(55)
  • 【数据结构】链表的分类和双向链表

    本篇是基于上篇单链表所作,推荐与上篇配合阅读,效果更加 http://t.csdnimg.cn/UhXEj 链表的结构非常多样,以下情况组合起来就有8种(2 x 2 x 2)链表结构: 我们一般叫这个头为哨兵位 我们上回讲的单链表就是不带头单项不循环链表。 今天我们要讲带头双向循环的链表。 不过

    2024年01月25日
    浏览(38)
  • 数据结构入门 — 链表详解_双向链表

    数据结构入门 — 双向链表详解 博客主页链接:https://blog.csdn.net/m0_74014525 关注博主,后期持续更新系列文章 文章末尾有源码 *****感谢观看,希望对你有所帮助***** 第一篇:数据结构入门 — 链表详解_单链表 第二篇:数据结构入门 — 链表详解_双向链表 第三篇:数据结构入门

    2024年02月11日
    浏览(47)
  • 数据结构:详解【链表】的实现(单向链表+双向链表)

    1.顺序表的问题和思考 问题: 中间/头部的插入删除,时间复杂度为O(N)。 增容需要申请新空间,拷贝数据,释放旧空间,会有不小的消耗。 增容一般是呈2倍的增长,势必会有一定的空间浪费。例如当前容量为100,满了以后增容到200,我们再继续插入了5个数据,后面没有数据

    2024年03月26日
    浏览(65)
  • 【数据结构与算法】双向链表

    作者:旧梦拾遗186 专栏:数据结构成长日记   带头双向循环链表:结构最复杂,一般用在单独存储数据。实际中使用的链表数据结构,都是带头双向循环链表。另外这个结构虽然结构复杂,但是使用代码实现以后会发现结构会带来很多优势,实现反而简单了。 现在我们来通

    2024年02月11日
    浏览(56)
  • 【数据结构】‘双向链表’冲冲冲

    💐 🌸 🌷 🍀 🌹 🌻 🌺 🍁 🍃 🍂 🌿 🍄🍝 🍛 🍤 📃 个人主页 :阿然成长日记 👈点击可跳转 📆 个人专栏: 🔹数据结构与算法🔹C语言进阶 🚩 不能则学,不知则问,耻于问人,决无长进 🍭 🍯 🍎 🍏 🍊 🍋 🍒 🍇 🍉 🍓 🍑 🍈 🍌 🍐 🍍 我们的节点是双向循

    2024年02月13日
    浏览(41)
  • 数据结构与算法:双向链表

    朋友们大家好啊,在上节完成单链表的讲解后,我们本篇文章来对 带头循环双向链表进行讲解 单链表中,一个节点存储数据和指向下一个节点的指针,而双向链表除了上述两个内容,还包括了 指向上一个节点的指针 带头的双向链表,是指在双向链表的最前端添加了一个 额

    2024年02月20日
    浏览(50)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包