【数据结构】-- 单链表 vs 双向链表

🌈 个人主页:白子寰

🔥 分类专栏:python从入门到精通,魔法指针,进阶C++,C语言,C语言题集,C语言实现游戏 👈 希望得到您的订阅和支持~

💡 坚持创作博文(平均质量分82+),分享更多关于深度学习、C/C++,python领域的优质内容!(希望得到您的关注~)

目录

单链表和双向链表的比较

链表的打印

单链表

双向链表

初始化

双向链表

开辟空间,写入新数据

单链表

双向链表

尾部插入数据

单链表

双向链表

头部插入数据

单链表

双向链表

尾部删除数据

单链表

双向链表

头部删除数据

单链表

双向链表

查找

单链表

双向链表

在指定位置之前插入数据

单链表

在指定位置之后插入数据

单链表

双向链表

删除指定位置数据

单链表

双向链表

删除指定位置后的数据

单链表

销毁链表

单链表

双向链表


单链表和双向链表的比较

|------|----------------------------------------------------------------|------------------------------------------|
| | 单向链表 | 双向链表 |
| 指代不同 | 链表的链接方向是单向的,访问链表时时要顺序读取从头开始访问 | 每个数据的节点都有两个指针,即直接前驱和直接后驱 |
| 优点不同 | 单个节点创建方便,普通的线性内存通常在创建的时候就需要设定数据的大小,节点的访问方便,可以通过循环/递归的方法访问到任意数据 | 从双向链表中的任意一个节点开始,方便访问前驱节点和后继节点 |
| 缺点不同 | 增加/删除节点复杂,需要多分配一个指针存储空间 | 平均访问效率低于线性表,只能从头到尾遍历,只能找到后继,无法找到前驱(只能前进) |

链表的打印

单链表

cs 复制代码
//链表的打印
void SLTPrint(SLTNode* phead)
{
	SLTNode* pcur = phead;
	while (pcur)
	{
		printf("%d->", pcur->data);
		pcur = pcur->next;
	}
	printf("NULL\n");
}

双向链表

cs 复制代码
//打印
void LTPrint(LTNode* phead)
{
	assert(phead);
	LTNode* pcur = phead->next;
	while (pcur != phead)
	{
		printf("%d->", pcur->dada);
		pcur = pcur->next;
	}
	printf("\n");
}

初始化

双向链表

cs 复制代码
//双向链表初始化
LTNode* LTInit()
{
	//哨兵位设置为-1
	LTNode* phead = SLTBuyNode(-1);
	return phead;
}

开辟空间,写入新数据

单链表

cs 复制代码
//开辟空间,写入新数据
SLTNode* SLTbuyNode(SLTDatatype x)
{
	SLTNode* newnode = (SLTNode*)malloc(sizeof(SLTNode));
	if (newnode == NULL)
	{
		perror("malloc");
		exit(1);
	}

	//先插入,后数据为空(后为空)
	newnode->data = x;
	newnode->next = NULL;

	return newnode;//返回数据
}

双向链表

cs 复制代码
//扩容,申请新节点
LTNode* SLTBuyNode(LTDataType x)
{
	LTNode* newnode = (LTNode*)malloc(sizeof(LTNode));//扩容
	if (newnode == NULL)
	{
		perror("malloc");
		exit(1);
	}
	//数据赋值
	newnode->dada = x;
	//指向自己
	newnode->next = newnode->prev = newnode;

	return newnode;
}

尾部插入数据

单链表

cs 复制代码
//尾插
void SLTPushBack(SLTNode** pphead, SLTDatatype x)
{
	//传的参数不为空
	assert(pphead);

	SLTNode*newnode = SLTbuyNode(x);
	//链表为空,新节点作为phead
	//*pphead是指向第一个节点的指针
	if (*pphead == NULL)//头节点为空
	{
		*pphead = newnode;
		return;
	}

	//链表不为空,找尾节点
	//ptail作为尾节点
	SLTNode* ptail = *pphead;
	//先方向,后数据
	while (ptail->next)
	{
		ptail = ptail->next;
	}
	ptail->next = newnode;
}

双向链表

cs 复制代码
//尾插
void LTPushBack(LTNode* phead, LTDataType x)
{
	assert(phead);
	LTNode* newnode = SLTBuyNode(x);
	
	newnode->next = phead;
	newnode->prev = phead->prev;

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

头部插入数据

单链表

cs 复制代码
//头插
void SLTPushFront(SLTNode** pphead, SLTDatatype x)
{
	//传参有效
	assert(pphead);
	//开辟空间,新数据进入
	SLTNode* newnode = SLTbuyNode(x);

	//先方向,后数据
	newnode->next = *pphead;
	*pphead = newnode;
}

双向链表

cs 复制代码
//头插
void LTPushFront(LTNode* phead, LTDataType x)
{
	assert(phead);
	LTNode* newnode = SLTBuyNode(x);

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

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

尾部删除数据

单链表

cs 复制代码
//尾删
void SLTPopBack(SLTNode** pphead)
{
	//保证传参数据有效
	assert(pphead);
	//链表不能为空
	assert(*pphead);

	//链表不为空
	//链表只有一个节点
	if ((*pphead)->next==NULL)
	{
		free(*pphead);
		*pphead = NULL;
		return;
	}

	//有多个节点
	SLTNode* prev = NULL;
	SLTNode* ptail = *pphead;
	while (prev->next)
	{
		prev = ptail;
		ptail = ptail->next;
	}
	prev = NULL;

	//销毁尾节点
	free(ptail);
	ptail = NULL;
}

双向链表

cs 复制代码
//尾删
void LTPopBack(LTNode* phead)
{
	assert(phead);

	LTNode* del = phead->prev;

	del->prev->next = phead;
	phead->prev = del->prev;

	free(del);
	del = NULL;
}

头部删除数据

单链表

cs 复制代码
//头删
void SLTPopFront(SLTNode** pphead)
{
	//保证传参有效
	assert(pphead);
	//保证链表有效
	assert(*pphead);

	//让第二个节点成为新的头
	SLTNode* next = (*pphead)->next;
	//把旧的头节点释放掉
	free(*pphead);

	*pphead = next;//第一个节点 = 原来第二个节点
}

双向链表

cs 复制代码
//头删
void LTPopFront(LTNode* phead)
{
	assert(phead);

	LTNode* del = phead->next;

	phead->next = del->next;
	del->next->prev = phead;

	free(del);
	del = NULL;
}

查找

单链表

cs 复制代码
SLTNode* SLTFind(SLTNode** pphead, SLTDatatype x)
{
	//保证传参有效
	assert(pphead);

	//遍历链表
	SLTNode* pcur = *pphead;
	while (pcur)
	{
		if (pcur->data == x)
		{
			return pcur;
		}
		pcur = pcur->next;
	}
	//没有找到
	return NULL;
}

双向链表

cs 复制代码
//查找
LTNode* LTFind(LTNode* phead, LTDataType x)
{
	assert(phead);

	LTNode* pcur = phead->next;

	while (pcur != phead)
	{
		if (pcur->dada == x)
		{
			return pcur;
		}
		pcur = pcur->next;
	}
	return NULL;
}

在指定位置之前插入数据

单链表

cs 复制代码
//在指定位置之前插入数据
void SLTInsert(SLTNode** pphead, SLTNode* pos, SLTDatatype x)
{
	//保证传参有效
	assert(pphead);
	//保证指定位置数据有效
	assert(pos);
	//链表也不能为空
	assert(*pphead);

	//新节点,开辟新空间
	SLTNode* newnode = SLTbuyNode(x);
	//pos刚好是头节点
	if (newnode->next = *pphead)
	{
		//头插
		SLTPushFront(pphead, x);
		return;
	}

	//pos不是头节点的情况
	SLTNode* prev = *pphead;
	if (prev->next != pos)
	{
		prev = prev->next;
	}
	prev->next = newnode;
	newnode->next = pos;
}

在指定位置之后插入数据

单链表

cs 复制代码
//在指定位置之后插入数据
void SLTInsertAfter(SLTNode* pos, SLTDatatype x)
{
	//保证目标数据有效
	assert(pos);

	//创建新节点
	SLTNode* newnode = SLTbuyNode(x);

	//先方向,后数据
	newnode->next = pos->next;
	pos->next = newnode;
}

双向链表

cs 复制代码
//在pos位置之后插入数据
void LTInsert(LTNode* pos, LTDataType x)
{
	assert(pos);

	LTNode* newnode = SLTBuyNode(x);
	newnode->prev = pos;
	newnode->next = pos->next;

	pos->next = newnode;
	pos->next->prev = newnode;
}

删除指定位置数据

单链表

cs 复制代码
//删除pos节点
void SLTErase(SLTNode** pphead, SLTNode* pos)
{
	//保证传的参数有效
	assert(pphead);
	//保证单链表有效
	assert(*pphead);
	//保证目标数据有效
	assert(pos);

	//pos刚好是头节点,没有前驱节点
	if (*pphead == pos)
	{
		//头删
		SLTPopFront(pphead);
		return;
	}

	//pos不是头节点
	SLTNode* prev = *pphead;
	while (prev->next != pos)
	{
		prev = prev->next;
	}

	//释放
	prev->next = pos->next;
	free(pos);
	pos = NULL;
}

双向链表

cs 复制代码
//删除pos位置的数据
void LTErase(LTNode* pos)
{
	assert(pos);

	LTNode* del = pos;
	pos->prev->next = pos->next;
	pos->next->prev = pos->prev;

	free(del);
	del = NULL;
}

删除指定位置后的数据

单链表

cs 复制代码
//删除pos之后的节点
void SLTEraseAfter(SLTNode* pos)
{
	//保证目标数据有效
	assert(pos);
	//pos->next数据有效
	assert(pos->next);

	SLTNode* del = pos->next;
	pos->next = pos->next->next;

	//释放
	free(del);
	del = NULL;
}

销毁链表

单链表

cs 复制代码
//销毁链表
void SListDesTroy(SLTNode** pphead)
{
	//保证传的参数有效
	assert(pphead);
	//保证链表有效
	assert(*pphead);

	SLTNode* pcur = *pphead;
	while (pcur)
	{
		SLTNode* next = pcur->next;
		free(pcur);
		pcur = next;
	}
	*pphead = NULL;
}

双向链表

cs 复制代码
//销毁
void LTDesTroy(LTNode* phead)
{
	//哨兵位不能为空
	assert(phead);

	LTNode* pcur = phead->next;
	while(pcur != phead)
	{
		LTNode* next = pcur->next;
		free(pcur);
		pcur = next;
	}
	free(phead);
	phead = NULL;
}

***********************************************************分割线*****************************************************************************
完结!!!
感谢浏览和阅读。

等等等等一下,分享最近喜欢的一句话:

"成为正确答案的第一步,相信自己会是正确答案"。

我是白子寰,如果你喜欢我的作品,不妨你留个点赞+关注让我知道你曾来过。
你的点赞和关注是我持续写作的动力!!!

好了划走吧。

相关推荐
ChoSeitaku9 分钟前
链表循环及差集相关算法题|判断循环双链表是否对称|两循环单链表合并成循环链表|使双向循环链表有序|单循环链表改双向循环链表|两链表的差集(C)
c语言·算法·链表
DdddJMs__13515 分钟前
C语言 | Leetcode C语言题解之第557题反转字符串中的单词III
c语言·leetcode·题解
娃娃丢没有坏心思1 小时前
C++20 概念与约束(2)—— 初识概念与约束
c语言·c++·现代c++
workflower1 小时前
数据结构练习题和答案
数据结构·算法·链表·线性回归
一个不喜欢and不会代码的码农1 小时前
力扣105:从先序和中序序列构造二叉树
数据结构·算法·leetcode
ahadee2 小时前
蓝桥杯每日真题 - 第11天
c语言·vscode·算法·蓝桥杯
No0d1es3 小时前
2024年9月青少年软件编程(C语言/C++)等级考试试卷(九级)
c语言·数据结构·c++·算法·青少年编程·电子学会
bingw01143 小时前
华为机试HJ42 学英语
数据结构·算法·华为
Che3rry4 小时前
C/C++|关于“子线程在堆中创建了资源但在资源未释放的情况下异常退出或挂掉”如何避免?
c语言·c++
Yanna_1234565 小时前
数据结构小项目
数据结构