数据结构-单链表-详解-2

news2024/9/21 23:41:20

数据结构-单链表-详解-2

  • 1.前言
  • 2.创建新结点
  • 3.头插与尾插
    • 3.1头插
    • 3.2尾插
      • 空链表
      • 找尾
  • 4.头删与尾删
    • 4.1头删
    • 4.2尾删

1.前言

在数据结构-单链表-详解-1中,我们不仅了解了单链表的基本概念,还掌握了如何创建和打印单链表。
今天,我将详细讲解如何对单链表进行头尾部的插入、删除。

2.创建新结点

在后续插入的过程中,都需要创建新的结点,因此直接将该过程封装为一个函数,便于操作。
SList.c

SListNode* BuySListNode(SLTDataType x)
{
	SLTNode* newnode = (SListNode*)malloc(sizeof(SListNode));
	if (newnode == NULL)
	{
		perror("BuySListNode::malloc");
		return NULL;
	}
	newnode->data = x;
	newnode->next = NULL;
	return newnode;
}

这段代码定义了一个名为BuySListNode的函数,它接受一个数据值作为参数,并返回一个新的链表结点。函数首先使用malloc来分配内存,然后初始化结点的数据成员和指向下一个结点的指针。

3.头插与尾插

链表的一个重要特性是可以在任意位置插入新元素,这使得链表非常适合动态数据集合的处理。
下面我将分别介绍如何实现头插和尾插操作。

3.1头插

  • 链表不为空:
2.断开
1.连接
2.连接
phead
结点一
结点二
...
NULL
newlist
  • 链表为空:
2.断开
1.连接
2.连接
phead
NULL
newlist

可以发现,头插时,链表为不为空操作相同。
SList.h

void SListPushFront(SListNode** pphead, SLTDataType x);

SList.c

void SListPushFront(SListNode** pphead, SLTDataType x)
{
	SListNode* newnode = BuySLTNode(x);
	newnode->next = *pphead;
	*pphead = newnode;
}

这段代码定义了一个名为SListPushFront的函数,它接受指向链表头结点的二级指针和一个数据值作为参数。
首先,调用CreateSLTNode来创建一个新结点,并将其next指针设置为原来的头结点。
最后,更新头指针使其指向新结点。
现在可以测试一下:
test.c:

void TestSList1()
{
	SListNode* plist = NULL;
	SListPushFront(&plist, 1);
	SListPushFront(&plist, 2);
	SListPushFront(&plist, 3);
	SListPushFront(&plist, 4);
	SListPrint(plist);
}
int main()
{
	TestSList1();
	return 0;
}

运行结果:
在这里插入图片描述

3.2尾插

空链表

在尾插中,如果链表为空,则需改变头指针,因此,我们传入二级指针。
SList.h

void SListPushBack(SListNode** pphead, SLTDataType x);

SList.c

void SListPushBack(SListNode** pphead, SLTDataType x)
{
	SListNode* newnode = BuySlistNode(x);
	*pphead = newnode;
}

找尾

SListNode* tail = *pphead;

尾插的实现比头插更复杂,想要在尾部插入数据,首先得找到尾部。
错误写法:

while (tail != NULL)
{
	tail = tail->next;
}
tail = newnode;

分析一下,循环的结束条件是tail等于NULL,而下一步则把新结点赋值给了NULL,因此错误。
链表的链接是让上一个结点有下一个结点的地址,因此,尾插的本质是:原尾结点中要存储新尾结点的地址。
完整写法:
SList.c

void SListPushBack(SListNode** pplist, SLTDataType x)
{
	SListNode* newnode = BuySlistNode(x);
	if (*pphead == NULL)
	{
		*pphead = newnode;
		return;
	}
	SListNode* tail = *pphead;
	while (tail->next != NULL)
	{
		tail = tail->next;
	}
	tail->next = newnode;
}

这段代码首先创建一个新结点。
如果链表为空,则直接插入新结点并更新头指针。
否则,初始化一个指针tail从头结点开始,并遍历链表直到找到最后一个结点。
最后,将新结点插入到链表的尾部。

4.头删与尾删

删除的条件是链表不为空,因此该处的函数开头应该assert断言。

4.1头删

两个以上的结点:

1.断开
1.连接
phead
结点一
结点二
...
NULL
2.free结点一

一个结点:

1.断开
1.连接
phead
结点一
NULL
2.free结点一

SList.h

void SListPopFront(SListNode** pphead);

SList.c

void SListPopFront(SListNode** pphead)
{
	assert(*pphead);
	SListNode* first = *pphead;
	*pphead = first->next;
	free(first);
	first = NULL;
}

这段代码定义了一个名为SListPopFront的函数,它接受指向链表头结点的二级指针作为参数。
函数首先使用assert来确保链表不为空,然后保存当前的头结点,并更新头指针使其指向下一个结点。
最后,释放原来头结点占用的内存,并将指针设为NULL以避免野指针。

4.2尾删

  • 单个结点:
    同头删
  • 多个结点:
    由于需要删除的是最后一个结点,因此,找尾时应该找倒数第二个结点。
    SList.h
void SListPopBack(SListNode** pphead);

SList.c

void SListPopBack(SListNode** pphead)
{
	assert(*pphead);
	if ((*pphead)->next == NULL)
	{
		free(*pphead);
		*pphead = NULL;
	}
	else
	{
		SListNode* tail = *pphead;
		while (tail->next->next != NULL)
		{
			tail = tail->next;
		}

		free(tail->next);
		tail->next = NULL;
	}
}

这段代码定义了一个名为SListPopBack的函数,它接受指向链表头结点的二级指针作为参数。
函数首先使用assert来确保链表不为空。
如果链表只有一个结点,则直接释放该结点并更新头指针。
否则,初始化一个指针tail从头结点开始,并遍历链表直到找到倒数第二个结点。
最后,释放尾结点占用的内存,并更新倒数第二个结点的next指针使其指向NULL
现在可以测试一下:
test.c:

void TestSList3()
{
	SListNode* plist = NULL;
	SListPushFront(&plist, 1);
	SListPushFront(&plist, 2);
	SListPushFront(&plist, 3);
	SListPushFront(&plist, 4);
	SListPrint(plist);
	SListPopFront(&plist);
	SListPrint(plist);
	SListPopBack(&plist);
	SListPrint(plist);
	SListPopFront(&plist);
	SListPrint(plist);
	SListPopBack(&plist);
	SListPrint(plist);
}

运行结果:
在这里插入图片描述


通过以上的介绍和代码实现,我们掌握了如何对单链表进行头尾部的插入、删除。希望这些内容能帮助你更好地理解单链表,并激发你进一步探索数据结构的兴趣!
本人仅是个C语言初学者,如果你有任何疑问或建议,欢迎随时留言讨论!让我们一起学习,共同进步!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2092350.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

QObject::moveToThread(QThread * targetThread)

改变该对象及其孩子的所在的线程。如果该对象有父亲,则它不能被移动。 事件处理将在targetThread中继续。 移动一个对象到主线程,使用QApplication::instance()来得到当前application的指针,使用QApplication()::thread()来得到应用程序所在…

springboot篇

文章目录 1.题目问答2.配置详情2.1配置文件2.2多环境配置2.3自定义参数2.4命令行参数2.5加载顺序 3.Starter POMs4.监控与管理—actuator4.1. 应用配置类4.1.1./autoconfg4.1.2. /beans4.1.3. /configprops4.1.4 /env4.1.5./mappings4.1.6./info 4.2. 度量指标类4.2.1. /metric…

Linux终端简单配置(Vim、oh-my-zsh和Terminator)

文章目录 0. 概述1. 完整Vim配置2. Vim配置方案解释2.1 状态行与配色方案2.2 文件管理与缓存设置2.3 搜索与导航优化2.4 缩进与格式化设置2.5 粘贴模式快捷切换2.6 文件编码与格式2.7 性能优化与备份 3. 安装 Oh My Zsh 及配置3.1 安装 Oh My Zsh3.2 Oh My Zsh 配置 3. Termina…

谷歌seo为什么要分析对手?

​简单来说,找到差距,弥补差距,提升网站 通过观察对手的网站内容、关键词策略和外链情况,是可以知道很多重要信息的,只要能熟练运用各种seo工具,通过分析对手网站是可以帮助优化自己网站的,研究…

进阶SpringBoot之 Shiro(2)环境搭建

Spring Boot 创建 Web 项目&#xff0c;pom.xml 导入 Thymeleaf 依赖 <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-thymeleaf</artifactId></dependency> resources 目录下 templates 包新…

VastBase——数据库参数调优

一、内存参数调优 数据库的复杂查询语句性能非常强的依赖于数据库系统内存的配置参数。数据库系统内存的配置参数主要包括逻辑内存管理的控制参数和执行算子是否下盘的参数&#xff1a; 1.逻辑内存管理参数&#xff1a;max_process_memory max_process_memory – shared memo…

SQL Server Profiler 工具的使用

在很多时候&#xff0c;我们需要优化接口的执行效率&#xff0c;一方面是提高代码在内存中的执行效率&#xff0c;另一方面就是提高数据库操作相关的效率了。 .NET中在System.Diagnostics类库下提供了Stopwatch类用来分析代码的执行耗时。那么如果是牵扯到数据库相关的操作&am…

前端速通面经八股系列(一)—— CSS篇

CSS高频面经目录 一、CSS基础1. CSS选择器及其优先级2. CSS中可继承与不可继承属性有哪些3. display的属性值及其作用4. display的block、inline和inline-block的区别5. 隐藏元素的方法有哪些6. link和import的区别7. transition和animation的区别8. display:none与visibility:…

Beyond Compare 4试用过期规避办法

声明: 支持正版软件 一: 注册表处理 在搜索栏中输入 regedit &#xff0c;打开注册表 删除项目&#xff1a;计算机\HKEY_CURRENT_USER\Software\ScooterSoftware\Beyond Compare 4\CacheId 二: 脚本处理 创建bat文件, 写入下面的命令行. reg delete “HKEY_CURRENT_USER\…

华为od全面介绍!!!

三分钟带你全面了解华为OD 【合同及管理】签约方为科锐国际/外企德科&#xff08;人力服务公司&#xff09;&#xff0c;劳动合同期为4年&#xff0c;试用期6个月。员工关系合同管理、五险一金、考勤发薪由科锐国际/外企德科负责&#xff1b;定级定薪、员工培训、工作安排、绩…

Redis的配置和启动+Redis Insight连接

一、安装 Redis的安装&#xff1a;从镜像站下载&#xff1a;索引 redis-local (huaweicloud.com)&#xff0c;然后将其传到Linux虚拟机中进行解压&#xff0c;解压之后需要下载gcc&#xff0c;因为Redis底层是用c写的&#xff0c;所以要编译一下生成redis文件&#xff0c;然后…

人工智能领域正经历模型规模变革,小型语言模型(SLM)崛起,挑战“规模至上”观念。

在人工智能领域&#xff0c;一场关于模型规模的深刻变革正在悄然发生。长久以来&#xff0c;科技巨头们热衷于庞大语言模型&#xff08;LLM&#xff09;的开发竞赛&#xff0c;但如今&#xff0c;小型语言模型&#xff08;SLM&#xff09;正以其独特的优势逐步崭露头角&#xf…

麦弗逊悬架KC特性分析APP开发与应用

汽车悬架系统是汽车重要的组成部分之一&#xff0c;起到支撑车身、减震降噪、提高行驶稳定性等多种作用。其中&#xff0c;悬架系统的K&C特性是一个重要指标&#xff0c;直接影响到汽车的操纵稳定性和乘坐舒适性&#xff0c;是悬架系统设计和优化的关键目标之一。 图1 汽车…

font-face 字体设置

摘要: font-face是css3中允许使用自定义字体的一个模块。 font-face的是一个CSS规则&#xff0c;允许你输入自己的字体出现在网站上&#xff0c;即使在特定的字体在访问者的计算机上没有安装。这条规则最重要的是&#xff0c;它为设计师打开了一个全新的世界。您可以使用任何你…

不平衡分类的成本敏感学习

不平衡分类的成本敏感学习 大多数机器学习算法都假设模型所犯的所有错误分类错误都是相同的。 对于不平衡分类问题&#xff0c;情况通常并非如此&#xff0c;因为遗漏正类或少数类案例比错误地将示例归类为负类或多数类更糟糕。有许多现实世界的例子&#xff0c;例如检测垃圾…

高效好用的10个自动化办公库分享

高效可用的10个自动化办公库 10个常用的Python自动化办公库本次内容涵盖了Excel、Word、PPT、ODF、PDF、邮件、微信、文件处理等所有能在办公场景实现自动化的库&#xff0c;希望能够对大家有所帮助。 PythonExcel自动化库 1.xlwings 库官网&#xff1a;https://www.xlwings…

用 Python 解锁电影台词中的秘密:给孩子一个学英语的新奇方式

引言 想象一下&#xff1a;孩子们不仅在看他们喜欢的电影&#xff0c;还能从中学到新的英语单词&#xff01;有没有什么比这更有趣、更高效的学习方式&#xff1f;在这篇博客中&#xff0c;我将带你一步步搭建一个 Python 工具&#xff0c;从电影台词中提取单词并生成详细的词…

06:网表更新到PCB

1.先了解HDMI设计规范 有4对差分线&#xff0c;1对IIC串行总线信号从芯片---->防静电器件—>HDMI输出 发现错误&#xff0c;没有SDA网络&#xff0c;检查原理图 重新导网表 3.完成信号芯片—>防静电模块—>HDMI布局

PMP–知识卡片--迭代型生命周期

迭代指的是多次循环。例如&#xff0c;软件开发按照版本发布&#xff0c;每一个版本内部都是一个小的瀑布开发&#xff0c;都会经历“需求分析—设计—开发—测试—发布”周期&#xff0c;下一个迭代在此基础上重复这些步骤&#xff0c;对软件进行优化升级&#xff0c;发布新的…

用友U8接口-自定义项和扩展自定义项(6)

概括 本文的操作需要正确部署U8API保存单据时传入自定义项和扩展自定义项 单据自定义项 ERP界面 接口参数 自定义项为标准erp字段&#xff0c;以cDefine开始的字段对应接口传入参数位置 [{"Inum": "OtherIn","Data": {"iHead": {&…