数据结构——双向链表
创始人
2024-12-14 06:08:17
0

目录

一、链表的分类

 (1)单向或双向​编辑

(2)带头或不带头​编辑

(3)循环或不循环​编辑

 (4)补充

二、实现双向链表

(1)List.h

(2)List.c

(3)注意

三、顺序表和链表的比较

 四、写在最后


一、链表的分类

链表的结构多种多样,包括:带头或不带头、单向或双向、循环或不循环,组合起来有8种情况(2x2x2):

 (1)单向或双向

(2)带头或不带头

(3)循环或不循环

 (4)补充

①虽然链表有多种结构,但是最常用的是单链表(不带头单向不循环链表)、双向链表(带头双向循环链表);

②带头指的是头结点,这里的头结点和我们之前说的是两个概念,实际上在前面的称呼并不严谨。头结点实际为“哨兵位”,哨兵位不存储有效数据,作用为“放哨的”;

③双向链表的结构

二、实现双向链表

(1)List.h

#include  #include  #include  #include   //定义双向链表的结点的结构 typedef int LTDatatype; typedef struct ListNode { 	LTDatatype data; 	struct ListNode* next; 	struct ListNode* prev; }LTNode;  //初始化 void LTInit1(LTNode** pphead); LTNode* LTInit2();  //打印 void LTPrint(LTNode* phead);  //插入 void LTPushFront(LTNode* phead, LTDatatype x); void LTPushBack(LTNode* phead, LTDatatype x);  //判断链表是否为空 bool LTEmpty(LTNode* phead);  //删除 void LTPopFront(LTNode* phead); void LTPopBack(LTNode* phead);  //查找数据位置 LTNode* LTFind(LTNode* phead, LTDatatype x);  //在指定位置之后插入数据 void LTInit(LTNode* pos, LTDatatype x);   //删除指定位置的数据 void LTErase(LTNode* pos);  //销毁 void LTDestroy(LTNode** pphead); void LTDestroy2(LTNode* phead);//需要手动将plist置为空

(2)List.c

#include "List.h"  LTNode* LTBuyNode(LTDatatype x) { 	LTNode* newnode = (LTNode*)malloc(sizeof(LTNode)); 	//如果申请不成功 	if (newnode == NULL) 	{ 		perror("malloc fail!\n"); 		exit(1);//退出 	} 	newnode->data = x; 	newnode->next = newnode->prev = newnode; 	//由于双向链表是循环的,所以指向自身而非NULL 	return newnode;//注意返回新结点 }  //初始化 void LTInit1(LTNode** pphead) { 	//创建哨兵位 	*pphead = LTBuyNode(-1); }  LTNode* LTInit2() { 	LTNode* phead = LTBuyNode(-1); 	return phead; }  //打印 void LTPrint(LTNode* phead) { 	LTNode* pcur = phead->next; 	while (pcur != phead) 	{ 		printf("%d->", pcur->data); 		pcur = pcur->next; 	} 	printf("\n"); }  //插入 void LTPushFront(LTNode* phead, LTDatatype x) { 	assert(phead); 	LTNode* newnode = LTBuyNode(x); 	newnode->next = phead->next; 	newnode->prev = phead; 	phead->next->prev = newnode; 	phead->next = newnode; }   void LTPushBack(LTNode* phead, LTDatatype x) { 	assert(phead); 	LTNode* newnode = LTBuyNode(x); 	newnode->next = phead; 	newnode->prev = phead->prev; 	phead->prev->next = newnode; 	phead->prev = newnode; }  //判断链表是否为空 bool LTEmpty(LTNode* phead) { 	assert(phead); 	return phead->next == phead; }   //删除 void LTPopFront(LTNode* phead) { 	assert(phead); 	assert(!LTEmpty(phead));//判断链表不为空 	LTNode* del = phead->next; 	phead->next = del->next; 	del->next->prev = phead; 	free(del); 	del = NULL; } void LTPopBack(LTNode* phead) { 	assert(phead); 	assert(!LTEmpty(phead));//判断链表不为空 	LTNode* del = phead->prev; 	del->prev->next = phead; 	phead->prev = del->prev; 	free(del); 	del = NULL; } //查找数据位置 LTNode* LTFind(LTNode* phead, LTDatatype x) { 	LTNode* pcur = phead->next; 	while (pcur != phead) 	{ 		if (pcur->data == x) 		{ 			return pcur; 		} 		pcur = pcur->next; 	} 	return NULL; }   //在指定位置之后插入数据 void LTInit(LTNode* pos, LTDatatype x) { 	assert(pos); 	LTNode* newnode = LTBuyNode(x); 	newnode->next = pos->next; 	newnode->prev = pos; 	pos->next->prev = newnode; 	pos->next = newnode; }  //删除指定位置的数据 void LTErase(LTNode* pos) { 	assert(pos); 	pos->prev->next = pos->next; 	pos->next->prev = pos->prev; 	free(pos); 	pos = NULL; }  //销毁 void LTDestroy(LTNode** pphead) { 	assert(pphead && *pphead); 	LTNode* pcur = (*pphead)->next; 	while (pcur != *pphead) 	{ 		LTNode* Next = pcur->next; 		free(pcur); 		pcur = Next; 	} 	free(*pphead); 	*pphead = NULL; 	pcur = NULL;//记得将pcur置为空,否则如果后续使用,它为野指针 }   void LTDestroy2(LTNode* phead) { 	assert(phead); 	LTNode* pcur = phead->next; 	while (pcur != phead) 	{ 		LTNode* Next = pcur->next; 		free(pcur); 		pcur = Next; 	} 	free(phead); 	phead = NULL; 	pcur = NULL; }

(3)注意

1.双向链表是循环的,在创建新结点时,新结点的next、prev指针要指向自身。因为如果链表为空,此时链表只有头节点,要想构成循环,其next、prev指针必须指向自身;

2.除初始化和销毁的函数传入的是二级指针外,其他函数的形参均为一级指针,为了保持接口的一致性,给它们传入一级指针作为优化。但美中不足的是,对于链表的销毁,还需在调用函数后将实参置为空;

3.插入时需要创建新结点,删除时需要判断链表是否为空。

三、顺序表和链表的比较

不同点顺序表链表(单链表)
存储空间上物理上一定连续逻辑上连续,但物理上不一定连续
随机访问支持,复杂度为O(1)不支持,复杂度为O(N)
任意位置插入或删除元素可能需要搬运元素,效率低,复杂度为O(N)只需要修改指针的指向
插入时的空间动态顺序表,空间不够时需要扩容,还可能造成空间浪费没有容量的概念,按需申请和释放,不存在空间浪费的情况
应用场景高效存储元素+频繁访问

任意位置高效插入和删除

 四、写在最后

我们的链表终于完结啦,撒花~~

敬请期待栈和队列!

相关内容

热门资讯

9分钟了解!浙江宝宝游戏辅助软... 9分钟了解!浙江宝宝游戏辅助软件!果然是真的有辅助工具(有挂透视)-哔哩哔哩小薇(辅助器软件下载)致...
透视挂!陕麻圈辅助器透视开挂&... 透视挂!陕麻圈辅助器透视开挂"详细辅助神器"原来真的是有挂(哔哩哔哩)1、完成陕麻圈辅助器透视开挂有...
第十分钟了解!丽水都莱有辅助吗... 第十分钟了解!丽水都莱有辅助吗!好像真的有辅助教程(发现有挂)-哔哩哔哩1、完成丽水都莱有辅助吗辅助...
2026版复盘!皮皮山西挖坑辅... 2026版复盘!皮皮山西挖坑辅助"普及辅助挂"真是真的是有挂(哔哩哔哩)进入游戏-大厅左侧-新手福利...
第2分钟了解!新九哥辅助工具!... 第2分钟了解!新九哥辅助工具!一贯有辅助攻略(有挂教学)-哔哩哔哩1、下载好新九哥辅助工具脚本下载之...
刚刚!微乐广西麻辣辅助器&qu... 刚刚!微乐广西麻辣辅助器"曝光辅助插件"其实是真的有挂(哔哩哔哩)1、打开软件启动之后找到中间准星的...
第八分钟了解!心悦俱乐部游戏辅... 第八分钟了解!心悦俱乐部游戏辅助!本来是真的有辅助神器(真的有挂)-哔哩哔哩1、第八分钟了解!心悦俱...
截至目前!小闲川南手游辅助器&... 截至目前!小闲川南手游辅助器"解密辅助攻略"一直确实有挂(哔哩哔哩)1、小闲川南手游辅助器免费脚本咨...
第7分钟了解!德州私人局脚本!... 第7分钟了解!德州私人局脚本!原来是有辅助app(有挂教程)-哔哩哔哩;1、完成德州私人局脚本辅助器...
第三方辅助挂!多乐手游辅助软件... 第三方辅助挂!多乐手游辅助软件"解密辅助器"本来存在有挂(哔哩哔哩)第三方辅助挂!多乐手游辅助软件"...