数据结构——单链表详解(超详细)(2)

news2024/11/13 9:25:03

前言:

  上一篇文章小编简单的介绍了单链表的概念和一些函数的实现,不过为了保证文章的简洁,小编把它分成了两篇来写,这一篇小编紧接上一篇文章继续写单链表函数功能的实现:

目录:

1.单链表剩余函数的编写

1.1.单链表查找数据

1.2.在指定位置之前插入数据

1.3.删除指定结点

1.4.在指定位置之后插入数据

1.5.删除指定位置之后的结点

1.6.单链表的销毁

2.完整的代码呈现

正文:

1.单链表剩余函数的编写

1.1.单链表查找数据

//查找

SLTNode* SLTFind(SLTNode* phead, SLdate x);

  对于单链表的查找数据,这就类似顺序表的查找数据,我们要遍历所有的单链表,直到找到我们想要的数据位置,加入没有我们想要的数据,那么我们可以直接返回一个空地址就好了,此时我们不必再传单链表指针的地址了,因为我们本质上并没有对头结点进行改变 ,所以我们仅需把头节点传过去就可以,在函数内部,我们想要遍历顺序表,就要采用循环,来进行一个一个结点的查找,下面是代码呈现:

SLTNode* SLTFind(SLTNode* phead, SLdate x)
{
	SLTNode* pour = phead;
	while (pour)
	{
		if (pour->date == x)
		{
			return pour;
		}
		pour = pour->next;
	}
	return NULL;
}

 1.2.在指定位置之前插入数据

    这里我们可以用到我们上面用到的查找函数,因为它是返回的结点,所以我们可以通过它来确定我们想要的指定位置,因为单链表只可以往后一步一步的走,而不能往前找数据,所以我们得先确认我们想要找的位置,下面我们涉及指定位置的时候,都要引用一下查找指定位置这个函数,下面我们来进行这个函数的讲解,这个函数其实也分为两种情况

  第一种情况是指定位置就是头结点,此时这就类似于我们之前写过的头插函数,我们直接引用头插函数就可以实现这个功能。

  第二种情况就是普通情况,我们要在指定位置之前插入数据,所以我们需要找到指定位置之前的数据,所以这里我们要用用到头结点,并且用到前面讲的创建新结点的函数(因为此文章涉及了不少插入数据的函数,所以我会把创建新结点的代码再写一遍!),我们让一个结点放入头结点数据,然后一步一步往后走,直到走到指定位置之前,之后我们可以讲新节点的下一个结点设置为指定结点,然后再把之前指定位置之前的结点的下一个结点设置为新结点,这里我们便可以完成在指定位置之后插入数据,下面小编先给上图文解释:

新结点的创建:

SLTNode* SLTbuynode(SLdate x)
{
	SLTNode* pour = (SLTNode*)malloc(sizeof(SLTNode));
	assert(pour);
	pour->date = x;
	pour->next = NULL;
	return pour;
}

头插函数: 

void SLTPushFront(SLTNode** pphead, SLdate x)
{
	assert(pphead);
	SLTNode* newnode = SLTbuynode(x);
	newnode->next = *pphead;
	*pphead = newnode;
}

函数部分:

void SLTInsert(SLTNode** pphead, SLTNode* pos, SLdate x)
{
	assert(pos && pphead && *pphead);
	if (pos == *pphead)
	{
		SLTPushFront(pphead,x);   //这个是头节点就是pos的特殊情况,我们在指定位置之前插入数据的时候,是会先找到指定位置之前的数据以及之后的,头节点的话找不到数据了
	}
	else
	{
		SLTNode* newnode = SLTbuynode(x);
		SLTNode* pour = *pphead;
		SLTNode* pur = NULL;
		while (pour != pos)
		{
			pur = pour;
			pour = pour->next;
		}
		assert(pur);
		newnode->next = pour;
		pur->next = newnode;
	}

1.3.删除指定结点

  删除指定结点的操作,同样也和上面的函数一样,我们也分为两种情况进行讨论:

  第一种情况就是我们想要删除的指定节点就是头结点,此时我们只需要调用一下头删函数就可以实现。

  第二种情况自然就是普通情况,此时我们依旧需要用到头结点,因为我们需要知道指定结点之前的结点,之后我们只需要让前一个结点的下一个结点直接指向指定结点的下一个结点,之后我们在把指定节点给释放掉,就可以实现指定节点删除操作,下面小编先给上图文解释:

头删函数:

void SLTPopFront(SLTNode** pphead)
{
	assert(pphead && *pphead);
	SLTNode* pour = *pphead;
	*pphead = (*pphead)->next;
	free(pour);
	pour = NULL;
}

删除指定结点函数:

void SLTErase(SLTNode** pphead, SLTNode* pos)
{
	assert(pphead && *pphead && pos <= *pphead);
	if (*pphead == pos)
	{
		SLTPopFront(pphead);
	}
	else
	{
		SLTNode* pour = *pphead;
		SLTNode* pur = NULL;
		while (pour != pos)
		{
			pur = pour;
			pour = pour->next;
		}
		assert(pur);
		pur -> next = pur->next -> next;
		free(pour);
		pour = NULL;
	}
}

1.4.在指定位置之后插入数据

  小编在上面已经讲述了指定位置之前如何插入数据了,那么现在可以讲述在指定位置之后插入数据了,这个其实比上面那个简单多了,因为此时我们不需要用到指定位置之前的结点了,所以我们不必在使用头结点了,此时我们依然需要先创建一个新节点,之后我们在讲新结点之后的结点变为我们指定位置之后的结点,指定位置之后的结点·在指向新节点,这里就可以实现在指定位置之后插入新数据了,下面小编先给上图文解释,再给上代码呈现: 

  

void SLTInsertAfter(SLTNode* pos, SLdate x)
{
	assert(pos);
	SLTNode* newnode = SLTbuynode(x);
	newnode->next = pos->next;
	pos->next = newnode;
}

1.5.删除指定位置之后的结点

  有插入必会有删除,下面我们将要讲述最后一个重要的函数,那么就是删除指定位置之后的结点,这个,同样也是非常的简单,我们只需要把指定结点的下一个结点变为下一个的下一个的结点,再把原来指定位置之后的结点释放掉就好了,下面我们直接给出图像说明:

void SLTEraseAfter(SLTNode* pos)
{
	assert(pos && pos->next);
	SLTNode* pour = pos -> next;
	pos->next = pos->next->next;
	free(pour);
	pour = NULL;
}

1.6.单链表的销毁

  单链表有创建,自然就会有销毁,单链表的销毁也不算很难,毕竟难的部分都在上面,轻舟已过万重山,对于单链表的销毁其实就是把每个结点给free掉,我们可以设置一个指针作为头结点,在设置一个指针用来给予上一个指针,每销毁完一个结点,我们把第二个指针的内容给予第一个指针,然后让第二个指针继续往后走,这样我们通过循环的知识可以做到每个结点的删除,循环结束完以后,别忘了对头结点进行销毁,这样我们便可以完成对于单链表的销毁了,下面直接给上代码:

void SListDestroy(SLTNode** pphead)
{
	assert(*pphead && pphead);
	SLTNode* pour = *pphead;
	while (pour)
	{
		SLTNode* next = pour->next;
		free(pour);
		pour = next;
	}
	*pphead = NULL;
}

2.完整的代码呈现

  现在,小编已经把单链表大部分内容给讲完了,但单链表实现的功能远不止这些,感兴趣的读者朋友可以再继续探索,小编这里给上大家单链表的完整代码:

SList.h

#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
typedef int SLdate;   //方便后面整体类型的改变


//创立一个单链表
typedef struct Slist {
	//先设置一个类型
	SLdate date;
	struct Slist* next;   //存放下一个节点的地址
}SLTNode;



//链表的打印
void SLTprintf(SLTNode* phead);

//开始正式环节了
//尾插
void SLTPushBack(SLTNode** pphead, SLdate x);

//头插
void SLTPushFront(SLTNode** pphead, SLdate x);

//尾删
void SLTPopBack(SLTNode** pphead);

//头删

void SLTPopFront(SLTNode** pphead);

//查找

SLTNode* SLTFind(SLTNode* phead, SLdate x);

//在指定位置之前插入数据

void SLTInsert(SLTNode** pphead, SLTNode* pos, SLdate x);

//删除pos节点

void SLTErase(SLTNode** pphead, SLTNode* pos);


//在指定位置以后插入数据

void SLTInsertAfter(SLTNode* pos, SLdate x);

//删除pos之后的节点

void SLTEraseAfter(SLTNode* pos);


//销毁链表
void SListDestroy(SLTNode** pphead);

SList.c

#include"SList.h"
void SLTprintf(SLTNode* phead)
{
	SLTNode* pour = phead;    //这么做是为了保证头节点不会发生改变
	while (pour)
	{
		printf("%d->", pour->date);
		pour = pour->next;
	}
	printf("NULL\n");
}   //这个操作是打印单链表的数据


SLTNode* SLTbuynode(SLdate x)
{
	SLTNode* pour = (SLTNode*)malloc(sizeof(SLTNode));
	assert(pour);
	pour->date = x;
	pour->next = NULL;
	return pour;
}

void SLTPushBack(SLTNode** pphead, SLdate x)
{
	//首先可以先建一个函数,这个函数是来开辟一个新节点的(后面想要插入直接调用就好了)
	assert(pphead);
	SLTNode * p = SLTbuynode(x);
	if (*pphead == NULL)  //首先判断
	{
		*pphead = p;
	}
	else
	{
		SLTNode* pour = *pphead;
		while (pour -> next)
		{
			pour = pour->next;
		}
		pour->next = p;
	}
}



void SLTPushFront(SLTNode** pphead, SLdate x)
{
	assert(pphead);
	SLTNode* newnode = SLTbuynode(x);
	newnode->next = *pphead;
	*pphead = newnode;
}



void SLTPopBack(SLTNode** pphead)
{
	assert(pphead && *pphead);
	if ((*pphead)->next == NULL)
	{
		*pphead = NULL;
	}
	else
	{
		SLTNode* pour = *pphead;
		SLTNode* plist = NULL;
		while (pour->next)
		{
			plist = pour;
			pour = pour->next;
		}
		plist->next = NULL;
		free(pour);
		pour = NULL;
	}
}



void SLTPopFront(SLTNode** pphead)
{
	assert(pphead && *pphead);
	SLTNode* pour = *pphead;
	*pphead = (*pphead)->next;
	free(pour);
	pour = NULL;
}



SLTNode* SLTFind(SLTNode* phead, SLdate x)
{
	SLTNode* pour = phead;
	while (pour)
	{
		if (pour->date == x)
		{
			return pour;
		}
		pour = pour->next;
	}
	return NULL;
}


void SLTInsert(SLTNode** pphead, SLTNode* pos, SLdate x)
{
	assert(pos && pphead && *pphead);
	if (pos == *pphead)
	{
		SLTPushFront(pphead,x);   //这个是头节点就是pos的特殊情况,我们在指定位置之前插入数据的时候,是会先找到指定位置之前的数据以及之后的,头节点的话找不到数据了
	}
	else
	{
		SLTNode* newnode = SLTbuynode(x);
		SLTNode* pour = *pphead;
		SLTNode* pur = NULL;
		while (pour != pos)
		{
			pur = pour;
			pour = pour->next;
		}
		assert(pur);
		newnode->next = pour;
		pur->next = newnode;
	}
}



void SLTErase(SLTNode** pphead, SLTNode* pos)
{
	assert(pphead && *pphead && pos <= *pphead);
	if (*pphead == pos)
	{
		SLTPopFront(pphead);
	}
	else
	{
		SLTNode* pour = *pphead;
		SLTNode* pur = NULL;
		while (pour != pos)
		{
			pur = pour;
			pour = pour->next;
		}
		assert(pur);
		pur -> next = pur->next -> next;
		free(pour);
		pour = NULL;
	}
}



void SLTInsertAfter(SLTNode* pos, SLdate x)
{
	assert(pos);
	SLTNode* newnode = SLTbuynode(x);
	newnode->next = pos->next;
	pos->next = newnode;
}



void SLTEraseAfter(SLTNode* pos)
{
	assert(pos && pos->next);
	SLTNode* pour = pos -> next;
	pos->next = pos->next->next;
	free(pour);
	pour = NULL;
}



void SListDestroy(SLTNode** pphead)
{
	assert(*pphead && pphead);
	SLTNode* pour = *pphead;
	while (pour)
	{
		SLTNode* next = pour->next;
		free(pour);
		pour = next;
	}
	*pphead = NULL;
}

总结:

  可算快马加鞭的肝完这篇文章了,小编本来想上一篇文章就结束单链表的,但是觉得万字文章有点太多了内容,于是分装成两篇来书写,因为小编是学完了单链表就开始写的文章,有一些地方可能有错误或者文笔显得很繁琐,各位读者朋友见谅,如果有错误的话,恳请在评论区指出,小编会及时的更正,那么,我们下一篇文章见啦! 

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

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

相关文章

Spring Security Oauth2源码分析

Spring Security Oauth2源码分析 前言一&#xff1a;客户端OAuth2授权请求的入口1、DefaultOAuth2AuthorizationRequestResolver类OAuth2AuthorizationRequest类authorizationRequestUri 的构建机制redirectUri 3、OAuth2AuthorizationRequestRedirectFilter类 二&#xff1a;O…

hiphop音乐风格分类有几种 怎么使用FL Studio制作Hip Hop音乐 hiphop音乐制作教程

Hip Hop音乐是一类新潮的音乐风格&#xff0c;融合了许多不同的文化元素和音乐表达方式。嘻哈&#xff08;hip hop&#xff09;诞生于美国贫民区街头的一种文化形式&#xff0c;一般的说法认为它诞生于美国纽约布朗克斯。嘻哈首先在纽约市北部布朗克斯市区的非裔及拉丁裔青年之…

kali进行host碰撞实验

目录 在kali上安装docker&#xff0c;完成环境搭建&#xff0c;进行host碰撞实验 更新软件包列表 下载阿里云的镜像源 添加 GPG 密钥并添加更新源 安装 Docker 安装成功检验 开启docker 检查docker状态 拉取镜像 在kali访问ip&#xff08;加端口&#xff09; 下载压缩…

京东超级18活动入口!京东超级18活动怎么玩?

京东推出新活动&#xff0c;低价的持续性项目“京东超级18”。 活动入口&#xff1a; 京东超级18&#xff0c;领60元大额补贴&#xff01; 口令直达&#xff1a;14:/京东超级18&#xffe5;NF5fuBPWIIaHRiMr&#xffe5;&#xff0c;↗鯨○Dσσδng。 手机京东搜索&#xf…

Claude 3.5 Sonnet模型发布,对比ChatGPT4o孰强孰弱

Anthropic 这家生而为打击 OpenAI 安全问题的公司&#xff0c;正式发布了Claude 3.5 Sonnet模型&#xff01; 用官网的话就是&#xff1a; 今天&#xff0c;我们推出了 Claude 3.5 Sonnet&#xff0c;这是我们即将推出的 Claude 3.5 型号系列中的第一个版本。Claude 3.5 Sonne…

vscode 打开远程bug vscode Failed to parse remote port from server output

vscode 打开远程bug vscode Failed to parse remote port from server output 原因如图&#xff1a; 解决&#xff1a;

02 Git环境搭建

第2章&#xff1a;Git环境搭建 一、Git下载和安装 ​ 官网&#xff1a;Git (git-scm.com) 一&#xff09;安装主程序 ​ 准备安装包&#xff0c;双击安装 ​ 开始安装 ​ 选择安装位置 ​ 选择需要安装的组件&#xff08;默认&#xff09; ​ 选择文件夹菜单 ​ 选择编辑器&…

Word参考文献交叉引用

前言 Word自带交叉引用功能&#xff0c;可在正文位置引用文档内自动编号的段落&#xff0c;同时创建超链接&#xff0c;适用于参考文献的引用。使用此方法对参考文献进行引用后&#xff0c;当参考文献的编号发生变化时&#xff0c;只需要更新域即可与正文中的引用相对应。下文…

sysbench测试CPU

版本&#xff1a;sysbench1.1 简介 sysbench 对 CPU 测试的原理是通过创建多个线程&#xff0c;并让它们执行计算密集型任务来测试 CPU 性能。它会记录每个线程执行任务所需的时间&#xff0c;并计算出整个测试过程的平均值、最小值和最大值&#xff0c;甚至输出直方图展示延迟…

SpringBoot下的定时魔法:揭秘@Scheduled注解的无限可能

在这个快节奏的时代&#xff0c;自动化与定时任务成为了提升效率的不二法门。而在Java的Spring Boot框架中&#xff0c;Scheduled注解就像是一位精通时间魔法的巫师&#xff0c;悄无声息地让你的应用按部就班地执行着各种定时任务。今天&#xff0c;就让我们一起揭开它的神秘面…

【Qt 常用控件】带你进一步了解常用控件

文章目录 1. Push Button2. Radio Button3. Check Box4. 显示类控件5. 输入类控件 QLineEdit5.1 正则表达式5.2 验证器 - 验证手机号5.3 验证两次密码是否一致5.3 明文的显示密码 1. Push Button &#x1f427;给按钮设置图标 &#x1f427; 注意&#xff1a;设置快捷键需要在…

【运维资料大全】运维全套资料整理(word原件完整版)

信息安全类、运维类资料整合&#xff1a; 1. 等保测评模板 2. 安全检查表&#xff08;IDS&#xff0c;Linux&#xff0c;数据库&#xff0c;Tomcat&#xff0c;防火墙等&#xff09; 3. 服务器安全巡检表 4. 网络定向攻击风险分析表 5. 应用系统常规检测表 6. 企业漏洞管理50个…

Web开发:卡片翻转效果(HTML、CSS)

目录 一、实现效果 二、完整代码 三、实现过程 1、页面结构 2、初始样式 3、翻转效果 4、图片大小问题 一、实现效果 如下图所示&#xff0c;当鼠标移入某个盒子&#xff0c;就反转这个盒子&#xff0c;并显示其背面的内容——卡片翻转效果&#xff1b; 卡片翻转效果 二…

【HarmonyOS】HarmonyOS NEXT学习日记:二、ArkTs语法

【HarmonyOS】HarmonyOS NEXT学习日记&#xff1a;二、ArkTs语法 众所周知TS是JS的超集,而ArkTs则可以理解为是Ts的超集。他们的基础都基于JS&#xff0c;所以学习之前最好就JS基础。我的学习重点也是放在ArkTs和JS的不同点上。 文章主要跟着官方文档学习&#xff0c;跳过了一…

Golang | Leetcode Golang题解之第234题回文链表

题目&#xff1a; 题解&#xff1a; func reverseList(head *ListNode) *ListNode {var prev, cur *ListNode nil, headfor cur ! nil {nextTmp : cur.Nextcur.Next prevprev curcur nextTmp}return prev }func endOfFirstHalf(head *ListNode) *ListNode {fast : headslo…

多媒体软件开发选择Animate软件还是Unity3D软件?

以下内容可能有一些片面&#xff0c;因为多媒体软件开发平台有很多&#xff0c;因为接触Animate和Unity3D比较多&#xff0c;所以这里仅对这两款进行分析&#xff01; Animate软件与Unity3D软件都是经常在多媒体展馆中用来制作互动展示内容的&#xff0c;对于这两种开发平台&a…

用AI生成Springboot单元测试代码太香了

你好&#xff0c;我是柳岸花开。 在当今软件开发过程中&#xff0c;单元测试已经成为保证代码质量的重要环节。然而&#xff0c;编写单元测试代码却常常让开发者头疼。幸运的是&#xff0c;随着AI技术的发展&#xff0c;我们可以利用AI工具来自动生成单元测试代码&#xff0c;极…

【系统架构设计师】十一、系统架构设计(中间件|典型应用架构)

目录 九、中间件 9.1 基础概念 9.2 中间件分类 十、典型应用架构 10.1 J2EE和四层结构 10.2 JSPServletJavaBeanDAO 10.3 .NET和J2EE之争 往期推荐 历年真题练习 九、中间件 之前总提到中间件&#xff0c;那么中间件到底是什么&#xff1f;在系统架构中又扮演者什么角…

摸鱼大数据——Kafka——kafka tools工具使用

可以在可视化的工具通过点击来操作kafka完成主题的创建&#xff0c;分区等操作 注意: 安装完后桌面不会有快捷方式,需要去电脑上搜索,或者去自己选的安装位置找到发送快捷方式到桌面! 连接配置 创建主题 删除主题 主题下的数据查看 数据显示问题说明 修改工具的数据显示类型 发…

【C++】vector的认识与使用

vector的认识与使用 认识vectorvector的使用Member functions&#xff08;成员函数&#xff09;构造函数(constructor)析构函数(destructor)赋值构造函数(operator) Iterators&#xff08;迭代器&#xff09;beginendrbeginrend Capacity&#xff08;容量&#xff09;sizemax_s…