数据结构 - 线性表 - 双向链表

一.链表的分类

链表的分类有3种分类,一共有8种(2*2*2):

(1)带头和不带头。

(2)单向和双向。

(3)循环和不循环

说明:

(1)单向链表只能从头结点遍历到尾结点,双向链表能从头走向为尾也能从

复制代码

二.常见的两种链表

**1.**单链表 - 不带头单向不循环链表。

**2.**双向链表 - 双向带头循环链表。

三.双向链表的结点

**1.结点组成:**保存的数据 + 指向下一个节点的指针 + 指向前一个结点的指针。

2.结点定义:

复制代码
typedef int LTDataType;
typedef struct ListNode {
	int data;
	struct ListNode* next;//指向后一个节点的指针
	struct ListNode* prev;//指向前一个结点的指针
}LTNode;

四.双向链表节点的创建

用 malloc 申请结点内存,若内存开辟失败就打印错误信息并结束程序,给结点存入数据 x,同时把该结点的 next 和 prev 指针都指向自身,形成自循环,最后返回新结点的地址。

复制代码
LTNode* LTBuyNode(LTDataType x)
{
	LTNode* newnode = malloc(sizeof(LTNode));
	if (newnode == NULL)
	{
		perror("malloc fail!");
		exit(1);
	}
	newnode->data = x;
	newnode->next = newnode->prev = newnode;

	return newnode;
}

五.双向链表的初始化

复制代码
LTNode* LTInit()
{
	LTNode* phead = LTBuyNode(-1);
	return phead;
}

六.双向链表的尾插

先断言检查头指针不为空,调用函数生成新结点,把新结点的前驱指向原来的尾结点、后继指向哨兵头结点,再修改原尾结点的后继和头结点的前驱,完成新结点接到链表尾部的双向链接。

复制代码
void LTPushBack(LTNode* phead, LTDataType x)
{
	assert(phead);

	LTNode* newnode = LTBuyNode(x);
	//phead phead->prev(尾结点) newnode
	newnode->prev = phead->prev;
	newnode->next = phead;

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

七.双向链表的头插

断言校验哨兵头指针有效后创建新结点,先设置新结点的前后指针,再修改原首结点的前驱和哨兵头的后继,把新结点插入到哨兵头结点之后,成为链表第一个有效节点。

复制代码
void LTPushFront(LTNode* phead, LTDataType x)
{
	assert(phead);
	LTNode* newnode = LTBuyNode(x);
	//phead newnode phead->next
	newnode->next = phead->next;
	newnode->prev = phead;

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

八.尾删

先断言确保链表不为空,拿到尾结点地址保存到 del,将尾结点的前一个结点的 next 指向哨兵头,再更新哨兵头的 prev 指向新的尾部结点,释放被删除结点的内存并置空局部指针,完成尾部结点的删除。

复制代码
void LTPopBack(LTNode* phead)
{
	assert(!LTEmpty(phead));
	LTNode* del = phead->prev;

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

	free(del);
	del = NULL;
}

九.头删

断言保证链表非空,把哨兵头后的第一个有效结点记为待删结点 del,修改后继结点的前驱和哨兵头的后继指针,跳过待删结点,释放该结点内存并将局部指针置空,完成链表头部元素删除。

复制代码
void LTPopFront(LTNode* phead, LTDataType x)
{
	assert(!LTEmpty(phead));
	LTNode* del = phead->next;
	//phead del del->next
	del->next->prev = phead;
	phead->next = del->next;

	free(del);
	del = NULL;
}

十.查找

复制代码
LTNode* LTFind(LTNode* phead, LTDataType x)
{
	assert(phead);
	LTNode* pcur = phead->next;
	while (pcur != phead)
	{
		if (pcur->data == x)
		{
			return pcur;
		}
		pcur = pcur->next;
	}
	//没找到
	return NULL;
}

十一.删除pos位置的结点

校验 pos 结点有效后创建新结点,先设置新结点的前后指针,再修改 pos 原后继结点的前驱与 pos 的后继指针,把新结点插入到 pos 结点的后面。

复制代码
void LTInsert(LTNode* pos, LTDataType x)
{
	assert(pos);
	LTNode* newnode = LTBuyNode(x);
	//pos newnode pos->next
	newnode->next = pos->next;
	newnode->prev = pos;

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

十二.删除pos位置的结点

断言确认待删结点 pos 有效,修改 pos 前后结点的双向指针,让前后结点直接相连绕过 pos,释放 pos 结点内存,将局部 pos 指针置空完成指定结点的删除。

复制代码
void LTErase(LTNode* pos)
{
	assert(pos);
	//pos->prev pos pos->next
	pos->next->prev = pos->prev;
	pos->prev->next = pos->next;

	free(pos);
	pos = NULL;
}

十三.销毁

复制代码
void LTDestroy(LTNode** pphead)
{
	LTNode* pcur = (*pphead)->next;
	while (pcur != *pphead)
	{
		LTNode* next = pcur->next;
		free(pcur);
		pcur = next;
	}
	free(*pphead);
	*pphead = NULL;
}
相关推荐
晓蛋16 小时前
c语言指的是什么意思
c语言·编译器·编程开发·集成开发环境·程序实例
一隅论数智16 小时前
给AI一张“业务概念地图“:本体如何从哲学走向企业智能
大数据·人工智能·经验分享·笔记·学习·学习方法·政务
傲世仙尊17 小时前
目录即文件-Ext文件系统收尾篇
linux·c语言
XiHongShi201617 小时前
STM32F407 RTC定时器例程,建议保存
stm32·单片机·学习
爱吃苹果的日记本17 小时前
离散数学第六课
学习·离散数学
牵猫散步的鱼儿17 小时前
重载、重写(覆盖)、重定义区别
c语言
phltxy18 小时前
C 语言指针:从内存地址到灵活的数据访问
c语言
m0_5474866618 小时前
《数据结构教程》全套 PPT课件2026
数据结构
phltxy19 小时前
C 语言中的数据存储:从类型到二进制位
c语言