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);
}
相关推荐
郝学胜-神的一滴1 小时前
深入解析Python字典的继承关系:从abc模块看设计之美
网络·数据结构·python·程序人生
HABuo2 小时前
【linux文件系统】磁盘结构&文件系统详谈
linux·运维·服务器·c语言·c++·ubuntu·centos
m0_736919103 小时前
C++代码风格检查工具
开发语言·c++·算法
2501_944934733 小时前
高职大数据技术专业,CDA和Python认证优先考哪个?
大数据·开发语言·python
黎雁·泠崖3 小时前
【魔法森林冒险】5/14 Allen类(三):任务进度与状态管理
java·开发语言
2301_763472464 小时前
C++20概念(Concepts)入门指南
开发语言·c++·算法
TechWJ5 小时前
PyPTO编程范式深度解读:让NPU开发像写Python一样简单
开发语言·python·cann·pypto
lly2024065 小时前
C++ 文件和流
开发语言
m0_706653235 小时前
分布式系统安全通信
开发语言·c++·算法
寻寻觅觅☆6 小时前
东华OJ-基础题-104-A == B ?(C++)
开发语言·c++