数据结构—单链表

含义

1.顺序表的回顾

之前的文章已经谈到了顺序表,关于顺序表,有一下的几种特点

1.中间,头部的删除,复杂度为O(N);

2.增容会有申请新的空间,拷贝数据,释放旧的空间,会有不小的消耗;

3:整容一般是呈2的倍数增长,但是也会造成一定的空间浪费,比如,当前的容量是100,满了以后·增加到200,我们继续插入5个数据,后面没有数据插入,就浪费了95个空间

2.链表的含义

那么,是否存在一种数据结构,能够解决上面的问题:

1.中间,头部数据的删除,不需要挪动数据

2.不需要扩容

3.不会造成空间的浪费

没错,就是今天的链表

链表的组成:

链表是由当前节点要存储的数据+下一个节点的地址组成的,就比如下面的这个

你可以把链表看成一节一节的火车车厢,想要加车厢的时候随时加,前面一个车厢掌管后面一个车厢的钥匙

放下一个节点的地址是为了方便找到下一个节点(这里有一点废话了哈),话不多说,上代码

typedef int SLdatetype;//链表的创建
struct Slist
{SLdatetype a;struct Slist* arr;
};

有的时候你觉得结构体的类型太长了,懒得写,以后在使用的时候也不太发表,可以换成下面的

typedef int SLdatetype;
typdef struct Slist
{SLdatetype a;struct Slist* arr;
}SL;
//下面的写法是不行的
typedef int SLdatetype;
typdef struct Slist
{SLdatetype a;SL* arr;//这种写法是不行的,因为编译器在执行程序的时候,是自上而下来扫描的,SL这种类型还没有//创建完毕
}SL;

链表的打印

#include"Slist.h"
void SLprint(SL* phead)
{SL* ptemp = phead;//建立临时的变量,防止下次要找头节点的时候找不到了while (ptemp!= NULL){printf("%d->", ptemp->date);ptemp = ptemp->next;}printf("NULL");
}

测试代码

#include"Slist.h"void test01()
{//在写的时候不建议这样写,这里只是为了测试//申请空间SL* node1 = (SL*)malloc(sizeof(SL));node1->date =1;SL* node2 = (SL*)malloc(sizeof(SL));node2->date =2;SL* node3 = (SL*)malloc(sizeof(SL));node3->date =3;//连接node1->next = node2;node2->next = node3;node3->next = NULL;SLprint(node1);
}int main()
{test01();return 0;
}

输出结果

链表的尾插

SL* SLbuynode(SLdatetype x)
{SL* newnode = (SL*)malloc(sizeof(SL));newnode->date = x;newnode->next = NULL;return newnode;
}
void SLpushback(SL** pphead, SLdatetype x)
{assert(pphead);//判断有效SL* pnew = SLbuynode(x);if (*pphead == NULL){*pphead = pnew;return;}SL* Ptail = *pphead;while (Ptail->next){Ptail = Ptail->next;}Ptail->next = pnew;
}

测试代码

void test01()
{SL* pptest = NULL;SLpushback(&pptest,1);SLprint(pptest);
}int main()
{test01();return 0;
}

代码说明

对上面的代码进行的几点说明

SLpushback(&pptest,1);

1.因为是对pphead进行的复制,所以上面函数进行的是传址调用,如果是传值调用的话,由于形参是实参的一份临时拷贝,所以在进行复制的时候,等到函数结束的时候形参销毁,该函数又没有返回值,所以对形参的改变并不会引起实参的改变

assert(pphead);//判断有效

2.断言部分,当你传入的地址是空指针的时候,编译其会报错,所以加上断言,但是我觉得具体原因是:当你传入空指针的时候,对该地址进行解引用的时候是找不到你要插入的位置的,就像别人给了你一张空头支票,你去银行去取钱,结果柜员一看,根本就找不到支票里面的钱在哪个地方一样,这是你的表情一定会和下面的一样

3.对于*pphead是否需要断言,答案是否定的,那里就是需要插入数据,因此是不需要断言的

链表的头插

void SLpushfront(SL** pphead, SLdatetype x)
{assert(pphead);SL*pnew= SLbuynode(x);pnew->next = *pphead;*pphead=pnew;
}

链表的尾删

void SLpopback(SL** pphead)
{assert(pphead);assert(*pphead);//链表只有一个节点的情况if ((*pphead)->next == NULL){*pphead = NULL;free(*pphead);return;}SL* tail = *pphead;SL* pre = NULL;//尾节点的前一个节点,在尾节点删除的时候,要将前面的节点的下一个节点置为空while (tail->next){pre = tail;//找到前驱节点tail = tail->next;}pre->next = NULL;free(tail);tail = NULL;}

链表的头删

关于链表的头删,只需要把第一个节点释放掉,然后然第二个节点变成第一个节点就行了

void SLpopfront(SL** pphead)
{SL* pcur = *pphead;*pphead= pcur->next;free(pcur);pcur = NULL;
}

 记住,在删除链表的头节点的时候,要先把头节点赋值给第二个节点,在对头节点进行释放,比如,写成下面的代码就不行

void SLpopfront(SL**pphead)
{freep(*pphead);*pphead=(*pphead)->next;
}
//因为已经释放了头节点,头节点已经变成了空指针
//空指针的下一个节点是不知道的

链表的查找

链表的查找和数组元素的查找是一样的,都是通过遍历的方式查找,找到了就返回当前的地址,没有找到就返回空指针

SL* SLfind(SL** pphead, SLdatetype x)
{assert(pphead);SL* pfind = *pphead;while (pfind){if (pfind->date == x){return pfind;//找到了,返回当前的指针}pfind = pfind->next;}return NULL;//没有找到,返回空指针
}

在指定位置之前插入数据

思路:情况一

            该链表只有一个节点,直接调用头插函数

            情况2:

            该链表有多个链表,通过遍历的方式找到前驱节点,改变前驱节点的指向

void SLpushposfront(SL** pphead, SL* pos, SLdatetype x)
{assert(pphead);assert(*pphead);SL* prev = *pphead;//定义前驱节点if ((*pphead)->next == NULL)//该链表只有一个节点{SLpushfront(pphead, x);return;}SL* pnew = SLbuynode( x);while (prev->next != pos){prev = prev->next;}//该节点为前驱节点prev->next = pnew;pnew->next = pos;
}

在指定位置之后插入节点

void SLpushposback(SL* pos, SLdatetype x)
{assert(pos);SL* posback = pos->next;//记录pos后面位置的节点SL* newnode = SLbuynode(x);pos->next = newnode;newnode->next = posback;
}

对上面代码的说明

为啥要在插入节点之前的步骤中先记录pos后面的节点嘞?

原因上面的最后一句代码

newnode->next = posback;

如果不记录的话,posback就变成了要插入的代码了,比如你写成这样

pos->next = newnode;
newnode->next = pos->next;//pos->next是newnode

删除pos节点

思路:情况一:多个节点找到先驱节点,改变先驱的指向,让它指向pos节点的下一个节点

              情况二:只有一个节点,执行头删或尾部删除

void SLerasepos(SL**pphead, SL* pos)
{assert(pphead);assert(*pphead);SL* prev = *pphead;//定义前驱节点if ((*pphead)->next == NULL)//只有一个节点,删除尾节点{SLpopfront(pphead);return;}while (prev->next != NULL){prev = prev->next;}//退出循环,前驱节点被找到prev->next = pos->next;free(pos);           pos = NULL;
}

注意:上面最后三行代码的顺序不能交换,不能写成下面的代码

free(pos);           
pos = NULL;
prev->next = pos->next;

原因是pos节点已经被释放,后面哪来的pos->next

删除pos之后的节点

void SLTEraseAfter(SLTNode* pos) {assert(pos);//pos->next不能为空assert(pos->next);//pos  pos->next  pos->next->nextSLTNode* del = pos->next;pos->next = pos->next->next;free(del);del = NULL;
}

摧毁链表

void SListDesTroy(SLTNode** pphead) {assert(pphead);assert(*pphead);SLTNode* pcur = *pphead;while (pcur){SLTNode* next = pcur->next;free(pcur);pcur = next;}*pphead = NULL;
}

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

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

相关文章

数据结构:最小生成树(Prim算法和Kruskal算法)、图的最短路径(Dijkstra算法和Bellman-Ford算法)

什么是最小生成树?Prim算法和Kruskal算法是如何找到最小生成树的? 最小生成树是指在一个连通图中,通过连接所有节点并使得总权重最小的子图。 Prim算法和Kruskal算法是两种常用的算法,用于寻找最小生成树。 Prim算法的步骤如下&…

mysql的多表查询和子查询

多表查询:查询数据时,需要使用多张表来查询 多表查询分类: 1.内连接查询 2.外连接查询 3.子查询 笛卡尔积: create table class (id int primary key auto_increment,name varchar(10) ); create table student (id int primar…

学习STM32第十六天

RTC实时时钟 一、简介 RTC是一个独立的BCD格式定时器,提供一个时钟日历,两个可编程报警中断,一个具有中断功能周期性可编程唤醒标志,RTC和时钟配置系统处于后备区域。 通过两个32位寄存器以BCD格式实现秒、分钟、小时&#xff08…

stable-diffusion-webui安装与使用过程中的遇到的error合集

stable-diffusion-webui1.9.2踩坑安装 1. 安装过程1.1 stable-diffusion-webui1.2 在win11或win10系统安装,需修改两个启动脚本1.2.1 修改webui-user.bat1.2.2 修改webui.bat 1.3 双击 webui-user.bat 启动脚本1.3.1 no module xformers. Processing without on fre…

Grafana系列 | Grafana监控TDengine库数据 |Grafana自定义Dashboard

开始前可以去grafana官网看看dashboard文档 https://grafana.com/docs/grafana/latest/dashboards 本文主要是监控TDengine库数据 目录 一、TDengine介绍二、Grafana监控TDengine数据三、Grafana自定义Dashboard 监控TDengine库数据1、grafana 变量2、添加变量3、配置panel 一…

FSMC读取FPGA的FIFO

一、硬件说明 FSMC配置 单片机的代码如下: #define VALUE_ADDRESS_AD1 (__IO uint16_t *)0x60400000while (1){if(!HAL_GPIO_ReadPin(GPIOF, GPIO_PIN_8)) //数据非空{data *(__IO uint16_t *)VALUE_ADDRESS_AD1;data2 *(__IO uint16_t *)VALUE_ADDRESS_AD1…

【数据库】MongoDB

文章目录 [toc]数据库操作查询数据库切换数据库查询当前数据库删除数据库查询数据库版本 数据集合操作创建数据集合查询数据集合删除数据集合 数据插入插入id重复的数据 数据更新数据更新一条丢失其他字段保留其他字段 数据批量更新 数据删除数据删除一条数据批量删除 数据查询…

Transformer step by step--Positional Embedding 和 Word Embedding

Transformer step by step往期文章: Transformer step by step--层归一化和批量归一化 要把Transformer中的Embedding说清楚,那就要说清楚Positional Embedding和Word Embedding。至于为什么有这两个Embedding,我们不妨看一眼Transformer的…

Hadoop之路

hadoop更适合在liunx环境下运行,会节省后期很多麻烦,而用虚拟器就太占主机内存了,因此后面我们将把hadoop安装到wsl后进行学习,后续学习的环境是Ubuntu-16.04 (windows上如何安装wsl) 千万强调,有的命令一…

【24年物联网华为杯】赛题分析与初步计划

赛事介绍 官网链接:2024 年全国大学生物联网设计竞赛 (sjtu.edu.cn) 含金量:属于A类赛事 (注意:很多搜索结果的序号是按照选入时间排列的,与含金量无关,华为杯是23年选入的) Kimi Chat: 全国…

JavaScript创建和填充数组的更多方法

空数组fill()方法创建并填充数组 ● 我们之前创建数组的方式都是手动去创建去一个数据,例如 console.log([1, 2, 3, 4, 5, 6, 7]);● 当然我们也可以使用Array对象来构造数组 console.log([1, 2, 3, 4, 5, 6, 7]); console.log(new Array(1, 2, 3, 4, 5, 6, 7));…

Java毕业设计 基于SpringBoot vue城镇保障性住房管理系统

Java毕业设计 基于SpringBoot vue城镇保障性住房管理系统 SpringBoot 城镇保障性住房管理系统 功能介绍 首页 图片轮播 房源信息 房源详情 申请房源 公示信息 公示详情 登录注册 个人中心 留言反馈 后台管理 登录 个人中心 修改密码 个人信息 用户管理 房屋类型 房源信息管理…

【算法基础实验】图论-UnionFind连通性检测之quick-find

Union-Find连通性检测之quick-find 理论基础 在图论和计算机科学中,Union-Find 或并查集是一种用于处理一组元素分成的多个不相交集合(即连通分量)的情况,并能快速回答这组元素中任意两个元素是否在同一集合中的问题。Union-Fin…

【React】Sigma.js框架网络图-入门篇(2)

通过《【React】Sigma.js框架网络图-入门篇》有了基本认识 由于上一篇直接给出了基本代码示例,可能看着比较复杂也不知道是啥意思; 今天从理论入手重新认识下! 一、基本认识 首先,我们先了解下基础术语: 图(Graph)&…

随笔 | 宿舍矛盾

室友A:睡觉时间比较早 室友B:睡觉时间比较晚,起床时间也晚 室友C:睡的晚,起的早 我:睡的时间随机,起的较早 事件1: 某一个星期四的中午,我正在听歌。室友C跟我说:我们去打扫卫生吧。于是&am…

CPPTest实例分析(C++ Test)

1 概述 CppTest是一个可移植、功能强大但简单的单元测试框架,用于处理C中的自动化测试。重点在于可用性和可扩展性。支持多种输出格式,并且可以轻松添加新的输出格式。 CppTest下载地址:下载地址1  下载地址2 下面结合实例分析下CppTest如…

【Linux网络】FTP服务

目录 一、FTP简介 1.FTP-文件传输协议 2.FTP的两种模式 二、安装配置FTP 1.安装环境 2.对文件的操作 3.切换目录 4.设置匿名用户 5.图形化界面登录 6.白名单与黑名单 重点与难点 一、FTP简介 1.FTP-文件传输协议 FTP是FileTransferProtocol(文件传输协…

【论文笔记 | 异步联邦】PORT:How Asynchronous can Federated Learning Be?

1. 论文信息 How Asynchronous can Federated Learning Be?2022 IEEE/ACM 30th International Symposium on Quality of Service (IWQoS). IEEE, 2022,不属于ccf认定 2. introduction 2.1. 背景: 现有的异步FL文献中设计的启发式方法都只反映设计空…

php反序列化字符串逃逸

字符串逃逸 字符串逃逸是通过改变序列化字符串的长度造成的php反序列化漏洞 一般是因为替换函数使得字符串长度发生变化,不论变长还是变短,原理都大致相同 在学习之前,要先了解序列化字符串的结构,在了解结构的基础上才能更好理解…

ASP.NET某企业信息管理系统的设计与实现

摘 要 信息管理系统就是我们常说的MIS(Management Information System),它是一个计算机软硬件资源以及数据库的人-机系统。经过对题目和内容的分析,选用了Microsoft公司的ASP.NET开发工具,由于它提供了用于从数据库中访问数据的强大工具集,使用它可以建立开发比较完善的数据库…