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);
}
相关推荐
中科三方14 分钟前
DNS拨测到底在拨测什么?
开发语言·github·php·dns·云拨测
软件黑马王子17 分钟前
20.Resources资源加载:基本实现
开发语言·前端框架·c#
李少兄41 分钟前
Java 中只有值传递吗?
java·开发语言·python
suhan1 小时前
【数据结构】二、线性表:2.单链表的定义与实现(带头结点与不带头结点的区别)
数据结构
可乐鸡翅yeah_1 小时前
hls.js 播放质量埋点实战,采集卡顿、起播、错误指标定位线上用户问题
开发语言·前端·javascript·ecmascript·音视频·m3u8·音视频在线播放
szarron1 小时前
RF Demo Kit|NanoVNA 射频演示测试板完整上手教程,滤波器、衰减器、SOLT 校准学习板
开发语言·人工智能·学习·php·射频工程·频谱仪
边境悍匪2 小时前
蜗牛学苑 Java 智能体学习 Day39|SpringAI 会话存储、Function‑Call 工具调用、MCP 思维导图复盘
java·开发语言·spring boot·学习·阿里云
万物智能信息科技2 小时前
RK3568 的多路显示移植—【万物智能之开源鸿蒙OpenHarmony系统实战开发系列教程】
linux·开发语言·华为·开源·harmonyos
蓝悦无人机2 小时前
Qt Design Studio(QDS)4.8.2项目文件结构详解
开发语言·qt·qt creator·qds·项目文件结构·qtc
yxlalm2 小时前
4.java秒杀项目第四课-后端商品后台接口
java·开发语言