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);
}
相关推荐
黄雪超2 小时前
JVM——函数式语法糖:如何使用Function、Stream来编写函数式程序?
java·开发语言·jvm
ThetaarSofVenice2 小时前
对象的finalization机制Test
java·开发语言·jvm
水木兰亭2 小时前
数据结构之——树及树的存储
数据结构·c++·学习·算法
思则变3 小时前
[Pytest] [Part 2]增加 log功能
开发语言·python·pytest
lijingguang3 小时前
在C#中根据URL下载文件并保存到本地,可以使用以下方法(推荐使用现代异步方式)
开发语言·c#
¥-oriented3 小时前
【C#中路径相关的概念】
开发语言·c#
Jess073 小时前
插入排序的简单介绍
数据结构·算法·排序算法
老一岁3 小时前
选择排序算法详解
数据结构·算法·排序算法
CoderCodingNo3 小时前
【GESP】C++四级考试大纲知识点梳理, (7) 排序算法基本概念
开发语言·c++·排序算法
恋猫de小郭4 小时前
Meta 宣布加入 Kotlin 基金会,将为 Kotlin 和 Android 生态提供全新支持
android·开发语言·ios·kotlin