【数据结构初级(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.hqwc.cn/news/163860.html

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

相关文章

ESP32 未来能够取代 STM32吗?

今日话题,ESP32 未来能够取代 STM32吗?ESP32和STM32各自有其特点和优势,能否取代彼此取决于具体应用和需求。STM32的流行除了性价比外,还有其强大的开发环境,例如Cubemx能够快速生成代码,使得上手STM32的速…

泛微移动管理平台E-mobile lang2sql接口任意文件上传漏洞

一、漏洞描述 泛微e-mobile,由高端OA泛微专业研发,是业内领先的移动OA系统,提供移动审批,移动考勤,移动报表,企业微信等丰富办公应用,支持多种平台运行,灵活易用安全性高。 e-mobile可满足企业日常管理中的绝大部分管理需求, 诸如市场销售、项目、采购、研发、客服…

Java设计模式——策略模式

1.策略模式简介 策略模式:策略模式是一种行为型模式,它将对象和行为分开,将行为定义为 一个行为接口 和 具体行为的实现。策略模式最大的特点是行为的变化,行为之间可以相互替换。每个if判断都可以理解为就是一个策略。本模式使得…

基于java web的计算机office课程平台设计与实现

末尾获取源码 开发语言:Java Java开发工具:JDK1.8 后端框架:SSM 前端:Vue 数据库:MySQL5.7和Navicat管理工具结合 服务器:Tomcat8.5 开发软件:IDEA / Eclipse 是否Maven项目:是 目录…

网工内推 | 售后工程师,IP认证优先,最高15薪,年底有分红

01 威发系统(中国)有限公司 招聘岗位:售后工程师 职责描述: 1、负责各种规模的项目售后安装、调试和维护工作; 2、解决工程和维护中的一般技术问题,支持、协助处理其他相关的技术问题; 3、与…

简单讲讲RISC-V跳转指令基于具体场景的实现

背景 在 RISC-V指令集中&#xff0c;一共有 6 条有条件跳转指令&#xff0c;分别是 beq、bne、blt、bltu、bge、bgeu。如下是它们的定义与接口 BEQ rs1, rs2, imm ≠ BNE rs1, rs2, imm &#xff1c; BLT rs1, rs2, imm ≥ BGE rs1, rs2, imm < unsigned BLTU rs1…

什么是高级持续性威胁(APT)?

【微|信|公|众|号&#xff1a;厦门微思网络】 厦门微思网络官网 高级持续性威胁&#xff08;Advanced Persistent Threat&#xff0c;APT&#xff09;&#xff0c;又叫高级长期威胁&#xff0c;是一种复杂的、持续的网络攻击&#xff0c;包含三个要素&#xff1a;高级、长期、…

安防监控EasyCVR视频汇聚平台使用海康SDK播放时,画面播放缓慢该如何解决?

视频云存储/安防监控EasyCVR视频汇聚平台基于云边端智能协同&#xff0c;支持海量视频的轻量化接入与汇聚、转码与处理、全网智能分发、视频集中存储等。安防视频平台EasyCVR拓展性强&#xff0c;视频能力丰富&#xff0c;具体可实现视频监控直播、视频轮播、视频录像、云存储、…

浅谈电力物联网时代物联网技术在电力系统中的应用

贾丽丽 安科瑞电气股份有限公司 上海嘉定201801 摘要&#xff1a;在电力系统建设中&#xff0c;物联网的应用不仅促进了我国电力工业的发展&#xff0c;而且对我国的物联网技术也起到了一定的促进作用。随着物联网技术应用于电力系统&#xff0c;推动了中国工业的快速发展。因…

bootstrap3简单玩法

Bootstrap v3 Bootstrap v3 是一个流行的前端框架&#xff0c;它提供了一系列的模板、组件和工具&#xff0c;可以帮助开发者快速地构建响应式的网站和应用程序。 以下是 Bootstrap v3 的一些常见应用&#xff1a; 响应式布局&#xff1a;Bootstrap v3 提供了一个易于使用的网…

测试工程师面试攻略:教你如何描述项目经验

俗话说的好&#xff0c;知己知彼百战百胜。项目经验说的好不好&#xff0c;直接决定了你值多少钱。满满的干货&#xff0c;先三连再耐心看完。 去面试之前&#xff0c;建议大家把简历写得从业经历和项目经验熟练的背下来&#xff0c;能说出项目经验里的详细的测试情况最好。 一…

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…