91.【C语言】数据结构之单向链表的头删和尾删

目录

1.尾删函数SLTPopBack

代码示例(写入SList.c)

在SList.h中写入该函数的声明

main.c部分代码改为

​编辑

分析

解决方法

方法1:双指针算法(快指针tail,慢指针pretail)

方法2

2.头删函数SLTPopFront

一个节点示意图

多个节点示意图

代码示例(写入SList.c)

在SList.h中写入该函数的声明

main.c部分代码改为


承接87.【C语言】数据结构之链表的头插和尾插文章

1.尾删函数SLTPopBack

代码示例(写入SList.c)

cpp 复制代码
void SLTPopBack(SLTNode** pphead)
{
	SLTNode* tail = *pphead;
	while (tail->next != NULL)
	{
		tail = tail->next;
	}
	free(tail);
	tail = NULL;
}

在SList.h中写入该函数的声明

main.c部分代码改为

cpp 复制代码
void TestSList1()
{
	SLTNode* plist = NULL;
	SLTPushBack(&plist, 1);
	SLTPushBack(&plist, 2);
	SLTPushBack(&plist, 3);
	SLTPushBack(&plist, 4);
	SLTPopBack(&plist);
	SLTPrint(plist);
}

运行后出了问题

分析

虽然为tail使用free函数并将tail置NULL,但是tail前面的节点含的指针并没有置NULL,导致其为野指针,因而出现了-572662307这样的随机值

解决方法

找到tail前的节点含的指针,并将其置NULL

方法1:双指针算法(快指针tail,慢指针pretail)

cpp 复制代码
void SLTPopBack(SLTNode** pphead)
{
	SLTNode* pretail = NULL;//初始化慢指针
	SLTNode* tail = *pphead;
	while (tail->next != NULL)
	{	
		pretail = tail;//待tail指针内容改变前,先赋值给慢指针
		tail = tail->next;
	}
	pretail->next = NULL;//tail的前一个节点含的指针置NULL

	free(tail);
	tail = NULL;
}

方法2

cpp 复制代码
void SLTPopBack(SLTNode** pphead)
{
	SLTNode* tail = *pphead;
	while (tail->next->next != NULL)
	{	
		tail = tail->next;
	}
	free(tail->next);
	tail->next = NULL;
}

tail->next->next != NULL//跨了一个节点

看起来没有问题,但是将main.c部分代码改成

cpp 复制代码
void TestSList1()
{
	SLTNode* plist = NULL;
	SLTPushBack(&plist, 1);
	SLTPushBack(&plist, 2);
	SLTPushBack(&plist, 3);
	SLTPushBack(&plist, 4);
	SLTPopBack(&plist);
	SLTPrint(plist);
	SLTPopBack(&plist);
	SLTPrint(plist);
	SLTPopBack(&plist);
	SLTPrint(plist);
	SLTPopBack(&plist);
	SLTPrint(plist);
}

就会出问题

当链表为1->NULL时,tail->next->next != NULL会导致读取访问权限冲突(原因:tail->next为NULL,NULL->next不合法)

如果用方法1,也会出现同样的问题

因此要在尾删函数的一开始做出判断:1.是否为空链表? 2.链表是否只有一个节点?

修正后的代码

cpp 复制代码
void SLTPopBack(SLTNode** pphead)
{
	assert(pphead);
	assert(*pphead);//检查是否为空链表,空链表不可以尾删
	//检查链表是否只有一个节点
	if ((*pphead)->next == NULL)
	{
		free(*pphead);//*pphead就是plist
	    *pphead = NULL;
	}
	else
	{
		SLTNode* tail = *pphead;
		while (tail->next->next != NULL)
		{
			tail = tail->next;
		}
		free(tail->next);
		tail->next = NULL;
	}
}

注意断言的顺序:先断言pphead,再断言*pphead!

2.头删函数SLTPopFront

和尾删函数一样:一开始做出判断:1.是否为空链表? 2.链表是否只有一个节点?

空链表不可头删,直接断言

一个节点示意图

多个节点示意图

一个节点和多个节点的处理方式可以合并在一起

代码示例(写入SList.c)

cpp 复制代码
void SLTPopFront(SLTNode** pphead)
{
	assert(pphead);
	assert(*pphead);//检查是否为空链表
	SLTNode* first = *pphead;
	*pphead = first->next;
	first = NULL;
}

注意断言的顺序:先断言pphead,再断言*pphead!

在SList.h中写入该函数的声明

main.c部分代码改为

cpp 复制代码
void TestSList1()
{
	SLTNode* plist = NULL;
	SLTPushBack(&plist, 1);
	SLTPushBack(&plist, 2);
	SLTPushBack(&plist, 3);
	SLTPushBack(&plist, 4);
	SLTPrint(plist);
	SLTPopFront(&plist);
	SLTPrint(plist);
	SLTPopFront(&plist);
	SLTPrint(plist);
	SLTPopFront(&plist);
	SLTPrint(plist);
	SLTPopFront(&plist);
	SLTPrint(plist);
}
相关推荐
Fairy要carry4 分钟前
面试-Agent上下文过载、步骤混乱的问题
开发语言·python
我能坚持多久8 分钟前
【初阶数据结构11】——链式二叉树知识补充
数据结构·算法
程序员Ctrl喵11 分钟前
异步编程:Event Loop 与 Isolate 的深层博弈
开发语言·flutter
liuyao_xianhui19 分钟前
优选算法_两数之和_位运算_C++
java·开发语言·数据结构·c++·算法·链表·动态规划
童话ing29 分钟前
【Golang】Golang Map数据结构底层原理
数据结构·golang·哈希算法
wWYy.30 分钟前
左值引用和右值引用
数据结构
IT猿手31 分钟前
MATLAB模拟四旋翼无人机飞行,机翼可独立旋转
开发语言·matlab·无人机
代龙涛42 分钟前
WordPress 主题开发指南:模板文件、函数与页面选型规则
开发语言·后端·php·wordpress
代码探秘者42 分钟前
【大模型应用】6.RAG 场景下的向量+关键词混合检索
java·开发语言·人工智能·python·spring