【数据结构初级(2)】单链表的基本操作和实现

文章目录

  • Ⅰ 概念及结构
    • 1. 单链表的概念
    • 2. 单链表的结构
  • Ⅱ 基本操作实现
    • 1. 定义单链表结点
    • 2. 创建新结点
    • 3. 单链表打印
    • 4. 单链表尾插
    • 5. 单链表头插
    • 6. 单链表尾删
    • 7. 单链表头删
    • 8. 单链表查找
    • 9. 在指定 pos 位置前插入结点
    • 10. 删除指定 pos 位置的结点
    • 11. 单链表销毁

本章实现的是不带头结点的单链表。

Ⅰ 概念及结构

1. 单链表的概念

  • 链表是一种物理存储结构上非连续、非顺序的存储结构,数据元素的逻辑顺序是通过链表中的指针链接次序实现的 。
  • 单链表的每个结点仅仅要存储该结点本身应该存储的数据,还要存储下一个与自己同类型结点的地址。

在这里插入图片描述

2. 单链表的结构

1. 物理结构

每个单链表结点都有一个指针域,用来存储下一个结点的地址。

在这里插入图片描述

2. 逻辑结构

为了方便理解和学习,使用箭头来表示每个结点的指针域所存储的是哪个结点的地址。

在这里插入图片描述

Ⅱ 基本操作实现

1. 定义单链表结点

  • 每个结点都应该包含数据域和指针域两部分内容。
  • 数据域的数据类型应该使用 typedef 来定义,防止以后更改数据域的数据类型。
typedef int SLTDataType;	//每个单链表结点数据域的数据类型typedef struct SListNode	//定义一个单链表结点
{SLTDataType data;		//单链表的数据域struct SListNode* next;	//单链表的指针域,用于存放下一个同类型结点的地址
}SLNode;					//单恋表的结点类型

2. 创建新结点

  • 开辟一个结点空间,将传过来的数据 x 放到结点的数据域,然后将该结点的指针域置 NULL。
SLNode* BuySListNode(SLTDataType x)
{SLNode* newnode = (SLNode*)malloc(sizeof(SLNode));	//开辟一个新结点空间if (NULL == newnode)	//如果开辟该结点失败{perror("malloc");exit(-1);}newnode->data = x;		//参数给新结点的数据域newnode->next = NULL;	//将新结点的指针域置空return newnode;			//返回开辟的新结点的地址
}

3. 单链表打印

1. 遍历链表

因为不能动指向首结点的指针,防止找不到首结点,所以此类操作都是利用额外的指针来实现的。

  • 使用一个指针 cur (current) 指向当前结点,如果 cur 不为空,则说明链表还没走到头。打印 cur 所指向的结点的数据域,然后让 cur 指针指向下一个结点。
  • 在打印的时候会有两种情况。
  1. 单链表为空:此时 cur 直接就指向 NULL,什么也不会打印。

在这里插入图片描述

  1. 单链表非空:打印 cur 指向结点的数据域的类容,然后将 cur 指向指向下一个结点,直到 cur 走到链表末尾后为止。

在这里插入图片描述

2. 代码实现

 //单链表打印
void SListPrint(SLNode* plist)
{SLNode* cur = plist;			//指向单链表的首结点while (cur != NULL)				//单链表还没走到尾{printf("%d->", cur->data);	//打印当前结点数据域的值cur = cur->next;			//继续指向下一个结点}printf("NULL\n");
}

3. 函数调用

SListPrint(plist);

4. 单链表尾插

1. 插入链表

将数据依次插入到单链表的尾部。
在进行单链表尾插时有两种情况:

  1. 单链表为空:直接创建一个新结点然后插入到单链表即可。

在这里插入图片描述

  1. 单链表非空:使用一个 tail 指针用来寻找单链表的尾部,要将新结点插入到 tail->next 为 NULL 的位置。

在这里插入图片描述

2. 代码实现

//传过来的是个指针变量的地址,需要使用二级指针 pplist 来接收
void SListPushBack(SLNode** pplist, SLTDataType x)
{assert(pplist);SLNode* newnode = BuySListNode(x);	//创建一个新的结点if (NULL == *pplist)				//单链表当前为空{*pplist= newnode;				//直接将新结点插入链表}else								//单链表非空{SLNode* tail = *pplist;			//tail 用于寻找单链表的尾结点while (tail->next != NULL)		//没有找到尾结点{tail = tail->next;			//指向下一个结点}tail->next = newnode;			//将新结点插入到尾结点之后,成为新的尾结点}
}

3. 函数调用

在这里插入图片描述

5. 单链表头插

1. 插入链表

  • 不管链表中有多少个结点,所有的数据都插入到链表的都一个位置

在这里插入图片描述

2. 代码实现

//要改变链表内容,实参要传指针变量的地址,形参用二级指针接收
void SListPushFront(SLNode** pplist, SLTDataType x)
{assert(pplist);						//传过来的不能是 NULL 指针SLNode* newnode = BuySListNode(x);	//创建新结点newnode->next = *pplist;			//将首结点地址交给新结点指针域*pplist= newnode;					//让新结点成为新的首结点
}

3. 调用函数

在这里插入图片描述

6. 单链表尾删

  • 每次将单链表尾部的的一个结点删除掉。

1. 实现方法

  1. 链表中只一个结点:直接释放链表指针指向的那个结点。

在这里插入图片描述

  1. 链表中有多个结点:使用一个 tail 指针寻找尾结点的前一个结点,如果 tail 所指向结点的下个结点的指针域为空,则说明 tail->next 指向的结点为尾结点,直接释放即可。

在这里插入图片描述

2. 实现代码

void SListPopBack(SLNode** pplist)
{assert(pplist);							//pplist 指向的 plist 不是 NULLassert(*pplist);						//plist 指向的链表不为空if (NULL == (*pplist)->next)			//1.一个结点{free(*pplist);						//释放链表中唯一的一个结点*pplist= NULL;}else									//2.多个结点{SLNode* tail = *pplist;				//使用 tail 找尾结点while (tail->next->next != NULL)	//当前结点的下下个结点不为空{tail = tail->next;				//tail 指向链表下一个结点}free(tail->next);					//释放 tail->next 指向的尾结点tail->next = NULL;					//将 tail->next 的值置空}
}

3. 函数调用

在这里插入图片描述

7. 单链表头删

  • 每次删除都只删除单链表的首结点。

1. 实现方法

  1. 链表有多个结点:先用一个临时指针变量保存首结点的地址,再让链表指针指向第二个结点,最后释放首结点。

在这里插入图片描述

  1. 链表只一个结点:步骤和上面相同,只不过只有一个结点时,链表指针在第二步会指向 NULL 而已。

2. 实现代码

void SListPopFront(SLNode** pplist)
{assert(pplist);				//传过来的不是 NULLassert(*pplist);			//链表不为空SLNode* tmp = *pplist;		//先保存链表首结点*pplist= (*pplist)->next;	//让链表指针指向第二个结点free(tmp);					//释放首结点
}

3. 函数调用

在这里插入图片描述

8. 单链表查找

  • 在单链表中查找数据域的值等于形参 x 的结点,并返回该结点的地址。
  • 如果链表中不存在 x 则返回 NULL。

实现代码

SLNode* SListFind(SLNode* plist, SLTDataType x)
{assert(plist);				//链表不为空SLNode* cur = plist;		//cur 用来访问当前结点while (cur != NULL)			//cur 没有走到链表末尾{if (cur->data == x)		//当前结点数据域的值等于形参 x{return cur;			//返回当前结点的地址}else					{cur = cur->next;	//继续往后找数据域等于 x 的结点}}return cur;					//链表中没有 x,返回 NULL
}

调用函数

在这里插入图片描述

9. 在指定 pos 位置前插入结点

1. 实现方法

  • 定义一个 cur (current) 指针指向当前结点,如果当前结点的下一个结点的地址等于 pos 的地址,则将 pos 指向的结点插入到新结点后,再将新结点插入到 cur 结点后。

在这里插入图片描述

2. 实现代码

void SLTInsert(SLNode** pplist, SLNode* pos, SLTDataType x)
{assert(pplist);						//传过来的不能是空指针assert((*pplist) && (pos));			//要么同时不为空assert(!(*pplist) && !(pos));		//要么同时为空SLNode* newnode = BuySListNode(x);	//创建新结点if (*pplist == pos)					//链表只有一个结点 - 头插{SListPopFront(pplist,x)			//新结点作首结点}else								//找 pos 位置的前一个{SLNode* cur = *pplist;			//cur 是 pos 位置的前一个结点while (cur->next != pos)		//当前结点的下个结点不是 pos {cur = cur->next;			//cur 向后寻找 pos 结点}newnode->next = pos;		//让新结点指针域指向 pos 结点cur->next = newnode;			//当前结点的指针域指向新结点}
}

调用函数

在这里插入图片描述

10. 删除指定 pos 位置的结点

1. 实现思路

  1. 在删除 pos 位置的结点前,先用一个前趋指针 pre 保存 pos 结点的前一个结点。
  2. 让 pre 指向 pos 的下一个结点。
  3. 销毁 pos 指向的结点。

在这里插入图片描述

2. 实现代码

void SLTErase(SLNode** pplist, SLNode* pos)
{assert(pplist);assert((*pplist) && (pos));		//两个指针同时不为空SLNode* pre = NULL;				//当前位置的前一个结点SLNode* cur = *pplist;			//用来寻找 pos 结点while (cur != NULL){if (cur != pos)			{pre = cur;cur = cur->next;}else						//cur 找到了 pos 指向的结点{pre->next = cur->next;	//pos 的前一个结点指向 pos 的后一个结点free(cur);				//删除 pos 指向的结点cur = NULL;}}
}

11. 单链表销毁

  • 对单链表连续执行头删。

实现代码

void SLTDestroy(SLNode** pplist)
{assert(pplist);SLNode* cur = *pplist;while (cur)						//链表不为空则执行头删{SLNode* next = cur->next;	//保存当前结点的下一个结点free(cur);					//释放当前结点cur = next;					//对下一个结点执行以上操作}*pplist = NULL;
}

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

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

相关文章

P02项目(学习)

★ P02项目 项目描述:安全操作项目旨在提高医疗设备的安全性,特别是在医生离开操作屏幕时,以减少非授权人员的误操作风险。为实现这一目标,我们采用多层次的保护措施,包括人脸识别、姿势检测以及二维码识别等技术。这些…

数据结构-邻接表广度优先搜索(C语言版)

对于一个有向图无向图,我们下面介绍第二种遍历方式。 广度优先搜索,即优先对同一层的顶点进行遍历。 如下图所示: 该例子,我们有六个顶点, 十条边。 对于广度优先搜索,我们先搜索a,再搜索abc…

5、Python中的变量和表达式:变量的定义、赋值和数据类型转换

文章目录 Python中的变量和表达式:变量的定义、赋值和数据类型转换变量的定义变量的赋值数据类型转换注意事项表达式总结Python中的变量和表达式:变量的定义、赋值和数据类型转换 Python是一种高级编程语言,以其简洁明了的语法和强大的功能而闻名。在Python编程中,变量和表…

力扣第121题 买卖股票的最佳时机 c++ 动态规划解法 熟练dp思维 之简单题 附Java代码

题目 (在我以前有贪心解法,也可以去参考参考) 贪心解法 股票问题https://blog.csdn.net/jgk666666/article/details/133978629 121. 买卖股票的最佳时机 简单 相关标签 数组 动态规划 给定一个数组 prices ,它的第 i 个元…

NVMe FDP会被广泛使用吗?

文章开头,我们需要先了解固态硬盘的读写机制。我们知道,固态硬盘的存储单元是由闪存颗粒组成的,无法实现物理性的数据覆盖,只能擦除然后写入,重复这一过程。因而,我们可以想象得到,在实际读写过…

Mac VsCode g++编译报错:不支持C++11语法解决

编译运行时报错: [Running] cd “/Users/yiran/Documents/vs_projects/c/” && g 1116.cpp -o 1116 && "/Users/yiran/Documents/vs_projects/c/"1116 1116.cpp:28:22: warning: range-based for loop is a C11 extension [-Wc11-extensi…

Maven3.9.1安装及环境变量配置

一、Maven的下载与安装 maven各版本下载地址 打开链接后自行选择对应版本 下载完成后解压安装,最好别选择c盘,安装目录路径等使用英文,避免产生其他问题 我这里选择的是D盘 二、Maven的环境变量配置 2.1、右键点击此电脑选择属性,点击高级系统设置,点…

jenkins结合k8s部署动态slave

1、完成k8s连接 在完成jenkins的部署后现安装kubernets的插件 如果jenkins 是部署在k8s集群中只需要填写一下 如果是非本集群的部署则需要填写证书等 cat ./config echo ‘certificate-authority-data-value’ | base64 -d > ./ca.crt echo ‘client-certificate-data’ |…

结合组件库实现table组件树状数据的增删改

如图所示&#xff0c;可以实现树状数据的新增子项&#xff0c;新增平级&#xff0c;删除。主要用到了递归 代码&#xff1a; <template><el-table :data"tableData" style"width: 100%; margin-bottom: 20px" row-key"id" border def…

uniapp使用技巧及例子

前言 uniapp&#xff08;Universal Application&#xff09;是一种基于Vue.js的全端解决方案&#xff0c;允许开发者使用一套代码构建多个平台的应用程序。这些平台包括iOS、Android、H5、微信小程序、支付宝小程序等。uniapp的出现解决了跨平台开发的痛点&#xff0c;大大减少…

ce从初阶到大牛--动态网络部署

1.基于域名www.openlab.com可以访问网站内容为 welcome to openlab!!! systemctl stop firewalld setenforce 0 cd /etc/httpd/conf.d/ vim openlab.conf ** <VirtualHost 192.168.170.100:80>DocumentRoot /www/openlabServerName 192.168.170.100 </VirtualHost>…

排序算法之-选择

算法原理 在未排序的数列中找出最大&#xff08;或最小&#xff09;的元素&#xff0c;然后将其存入到已排序的数列起始位置&#xff0c;紧接着在剩余的未排序数列中继续查找最大&#xff08;或最小&#xff09;的元素&#xff0c;并将其放入到已排序的数列末尾&#xff0c;依…

行情分析——加密货币市场大盘走势(11.6)

大饼昨日下跌过后开始有回调的迹象&#xff0c;现在还是在做指标修复&#xff0c;大饼的策略保持逢低做多。稳健的依然是不碰&#xff0c;目前涨不上去&#xff0c;跌不下来。 以太周五给的策略&#xff0c;入场的已经止盈了&#xff0c;现在已经达到1884&#xff0c;已经全部吃…

Java —— 类和对象(一)

目录 1. 面向对象的初步认知 1.1 什么是面向对象 1.2 面向对象与面向过程 2. 类定义和使用 2.1 认识类 2.2 类的定义格式 3. 类的实例化(如何产生对象) 3.1 什么是实例化 3.2 访问对象的成员 3.3 类和对象的说明 4. this引用 4.1 为什么要有this引用 4.2 什么是this引用 4.3 th…

十一、K8S之持久化存储

持久化存储 一、概念 在K8S中&#xff0c;数据持久化可以让容器在重新调度、重启或者迁移时保留其数据&#xff0c;并且确保数据的可靠性和持久性。 持久化存储通常用于程序的状态数据、数据库文件、日志文件等需要在容器生命周期之外的数据&#xff0c;它可以通过各种存储解…

3.27每日一题(常系数线性非齐次方程的特解)

常系数非齐次线性方程的特解如何假设&#xff08;两种&#xff09;形式&#xff1a; 1、题目中 e 的 x 次幂以及 1&#xff0c;都是第一种&#xff1a;1可以看成为e的0次幂 注&#xff1a;题目给的多项式是特殊的形式&#xff0c;我们要设为一般的形式的多项式 2、题目中sin…

css基础之实现轮播图

原理介绍 图片轮播的原理是通过控制显示和隐藏不同的图片来实现图像的切换&#xff0c;从而创建连续播放的效果。用到的知识点有定位和定时器。 实现步骤&#xff1a; HTML 结构&#xff1a; 首先&#xff0c;需要在HTML中创建一个包含轮播图片的容器&#xff0c;通常使用 &l…

Verilog使用vscode

使用vscode打开.v文件 Tools setting texteditor vscode文件路径 [line number]:[file name] &#xff08;可能会出错&#xff0c;可以去vscode确认打开的文件路径&#xff0c;后经调整后改为 vscode文件路径 [file name]&#xff09; 安装插件 搜索Verilog 添加使用最多的 …

Vue3:解决基地址不同 数据交互http与https跨域问题

配置公共管理的api文件和vue.config.js可以解决跨域问题。一个项目对接不同的基地址和接口同理。 api export default {//接口基地址Millia: process.env.NODE_ENV development ? location.protocol // location.host /milliaApi : http://xx.xxx.xxxx/index.php/,Milli…

线上 kafka rebalance 解决

上周末我们服务上线完毕之后发生了一个kafka相关的异常&#xff0c;线上的kafka频繁的rebalance&#xff0c;详细的报错我已经贴到下面&#xff0c;根据字面意思&#xff1a;消费者异常 org.apache.kafka.clients.consumer.CommitFailedException: 无法完成提交&#xff0c;因为…