数据结构【没头单链表】

目录

概念与结构

结点

链表的性质

链表的打印分析

实现单链表:

创建单链表数据

申请空间

尾插数据

打印

头插数据

尾删

头删

查询数据

指定位置前插入数据

指定位置后插入数据

删除pos节点

删除pos后面的节点

销毁

链表的分类

链表说明:


概念与结构

概念:链表是⼀种物理存储结构上⾮连续、⾮顺序的存储结构,数据元素的逻辑顺序是通过链表中的 指针链接次序实现的。

 这个单链表我们需要一个整行或其他类型的存放数据,还有一个结构体指针,结构体指针连接下一个节点。

结点

与顺序表不同的是,链表⾥的每节"⻋厢"都是独⽴申请下来的空间,我们称之为“结点/结点” 

结点的组成主要有两个部分:当前结点要保存的数据和保存下⼀个结点的地址(指针变量)。

图中指针变量plist保存的是第⼀个结点的地址,我们称plist此时“指向”第⼀个结点,如果我们希望 plist“指向”第⼆个结点时,只需要修改plist保存的内容为0x0012FFA0。 链表中每个结点都是独⽴申请的(即需要插⼊数据时才去申请⼀块结点的空间),我们需要通过指针 变量来保存下⼀个结点位置才能从当前结点找到下⼀个结点。

链表的性质

1、链式机构在逻辑上是连续的,在物理结构上不⼀定连续

2、结点⼀般是从堆上申请的

 3、从堆上申请来的空间,是按照⼀定策略分配出来的,每次申请的空间可能连续,可能不连续

结合前⾯学到的结构体知识,我们可以给出每个结点对应的结构体代码:

假设当前保存的结点为整型:

struct SListNode
{int data; //节点数据struct SListNode* next; //指针变量⽤保存下⼀个节点的地址
};

当我们想要保存⼀个整型数据时,实际是向操作系统申请了⼀块内存,这个内存不仅要保存整型数 据,也需要保存下⼀个节点的地址(当下⼀个节点为空时保存的地址为空)。 当我们想要从第⼀个节点⾛到最后⼀个节点时,只需要在当前节点拿上下⼀个结点的地址就可以了。

链表的打印分析

给定的链表结构中,如何实现结点从头到尾的打印?

思考:当我们想保存的数据类型为字符型、浮点型或者其他⾃定义的类型时,该如何修改?

实现单链表:

创建3个文件,slist.h头文件,slist.c存放函数的文件,test.c测试文件


创建单链表数据

arr用来存放数据,p指向下一个节点

#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
typedef int data;
typedef struct slist
{//存放数据data* arr;//指向下一个节点struct slist* p;
}SL;

在测试文件创建链表为NULL


申请空间

接收的是数值,返回的是空间,把arr赋值x,p给空返回add

//申请空间
SL* koj(data x)
{//申请空间SL* add = (SL*)malloc(sizeof(SL));//判断是不是空if (add == NULL){perror("malloc");exit(1);}//把数据赋值给addadd->arr = x;add->p = NULL;//然后返回return add;
}

尾插数据

头文件

使用二级指针接收,直接修改实参。用一级的话还要赋值。

声明尾插的函数,为什么要声明,因为如果我们有很多个函数的话要一个一个找很麻烦,所以声明在头文件,就像我们看书的目录一样方便我们查看有哪些函数。

//尾插
void weic(SL** r, data x);


add接收的新申请的空间

思路:先判断当前r是不是空,是就直接把申请的空间给过去就行了

有空间,把地址给tab循环走到最后一个节点,然后让最后一个节点的指针指向add空间

//尾插
void weic(SL** r, data x)
{assert(r);//申请空间SL* add = koj(x);//判断单链表是不是空if (*r == NULL){//是空就直接赋值*r = add;}//不是空就循环走到tab->p 的null位置,进行连接else{//把r地址给tabSL* tab = *r;while (tab->p){//循环走到tab->p 的null位置,进行连接tab = tab->p;}//把申请的空间和tab->p连接tab->p = add;}
}

打印

打印用一级就行了,我们只是打印数据。

//打印
void day(SL* r);

打印

循环往后打印,最后一个打印NULL

//打印
void day(SL* r)
{SL* add = r;while (add){printf("%d->", add->arr);add = add->p;}printf("NULL\n");
}

    SL* add = NULL;//尾插weic(&add, 1);weic(&add, 2);weic(&add, 3);weic(&add, 4);

头插数据

//头插
void toc(SL** r, data x);


思路:申请add空间,add的指针指向头节点*r,把add给*r这样新申请的空间就是头节点了。

//头插
void toc(SL** r, data x)
{assert(r);//申请空间SL* add = koj(x);//把申请空间的add->p指向r的当前地址add->p = *r;//把add的地址给r*r = add;
}

    SL* add = NULL;//头插toc(&add, 1);toc(&add, 2);toc(&add, 3);toc(&add, 4);

尾删

//尾删除
void weisc(SL** r);


思路:判断当前节点的下一个节点是不是空,是就说明只有一个节点,直接释放。

循环往后走到最后一个节点,每走一步前保存到tab,就能拿到前一个节点了。

把tab->p赋值为空,释放add空间就可以了。

//尾删除
void weisc(SL** r)
{assert(r && *r);//判断第一个节点的p是不是null,是就直接释放if ((*r)->p == NULL){free(*r);*r = NULL;}else{//这个找后面的节点SL* add = *r;//这个是后面的前一个节点SL* tab = NULL;//循环到后面的节点while (add->p){//把当前节点给tabtab = add;//然后指向下一个节点add = add->p;}//把tab赋值为nulltab->p = NULL;//然后释放add的空间free(add);add = NULL;}
}

我们可以看到尾插1,2,3,4删除了4这个节点

    SL* add = NULL;//尾插weic(&add, 1);weic(&add, 2);weic(&add, 3);weic(&add, 4);//尾删weisc(&add);day(add);

头删

//头删除
void tosc(SL** r);


思路:创建add保存头节点,r往后走一步,释放add空间

//头删除
void tosc(SL** r)
{assert(*r);SL* add = *r;*r = (*r)->p;free(add);add = NULL;
}

可以看到我们删了1和2。

	SL* add = NULL;//尾插weic(&add, 1);weic(&add, 2);weic(&add, 3);weic(&add, 4);//头删tosc(&add);tosc(&add);day(add);

查询数据

//查询
SL* cx(SL* r,data x);

思路:把r给add,让add循环判断每个节点的arr数据等不等于x,等于直接返回当前节点,不等于返回NULL

//查询
SL* cx(SL* r, data x)
{SL* add = r;//循环查询while (add){//判断是不是等于xif (add->arr == x){//是就返回当前节点return add;}//指向下一个节点add = add->p;}//没有返回空return NULL;
}

pos接收当前节点,等于空打印没找到,不等于打印找到了。

	SL* add = NULL;//尾插weic(&add, 1);weic(&add, 2);weic(&add, 3);weic(&add, 4);day(add);//查询SL* pos = cx(add, 4);if (pos == NULL){printf("没找到\n");}else{printf("找到了\n");}

指定位置前插入数据

//在指定位置前插入数据
void zhidqcr(SL** r, SL* pos, data x);


思路:判断pos等于r就说明在第一个节点或只有一个节点,直接调用头插的函数就行了。

add循环到pos后面,tab->p连接pos,add->p连接tab。

//在指定位置前插入数据
void zhidqcr(SL** r,SL* pos, data x)
{assert(r);assert(pos);//判断是不是在第一个节点if (pos == *r){//是就调用头插toc(r,x);}else{//申请的空间SL* tab = koj(x);SL* add = *r;//循环到节点前面停下while (add->p != pos){add = add->p;}//进行tab->p = pos;add->p = tab;}
}

在3的前面插入了一个数据为99的节点

	SL* add = NULL;//尾插weic(&add, 1);weic(&add, 2);weic(&add, 3);weic(&add, 4);//查询SL* pos = cx(add, 3);//if (pos == NULL)//{//	printf("没找到\n");//}//else//{//	printf("找到了\n");//}zhidqcr(&add, pos, 99);day(add);

指定位置后插入数据

//在指定位置后插入数据
void zhidhcr(SL* pos, data x);


思路:add是新申请的空间,add->p连接pos下一个节点,pos->p连接add。

//指定位置后插入数据
void zhidhcr(SL* pos, data x)
{assert(pos);//申请空间SL* add = koj(x);//把r后面那个节点的地址给新的节点add->p = pos->p;//把新的节点给rpos->p = add;
}

我们可以看到在3后面插入了一个99的节点。

	SL* add = NULL;//尾插weic(&add, 1);weic(&add, 2);weic(&add, 3);weic(&add, 4);//查询SL* pos = cx(add, 3);//if (pos == NULL)//{//	printf("没找到\n");//}//else//{//	printf("找到了\n");//}zhidhcr(pos, 99);

删除pos节点

//删除pos节点
void scpos(SL** r, SL* pos);


思路:判断只有一个节点或等于当前节点,直接释放。

把r给add,让add循环到pos前一个节点,add->p指向pos下一个节点,然后释放pos空间。

//删除pos节点
void scpos(SL** r, SL* pos)
{//我们需要pos前一个节点,和后一个节点连接assert(r && *r);assert(pos);//判断如果(第一个节点等于要删除的节点)直接释放if (*r == pos){free(*r);*r = NULL;}else{SL* add = *r;//循环走到pos节点的后面while (add->p != pos){add = add->p;}//把pos指向的节点,给pos前面的节点(把pos后面的节点和pos前面的节点进行连接)add->p = pos->p;//释放pos空间free(pos);pos = NULL;}
}

我们可以看到3被删除了

    SL* add = NULL;//尾插weic(&add, 1);weic(&add, 2);weic(&add, 3);weic(&add, 4);//查询SL* pos = cx(add, 3);//if (pos == NULL)//{//	printf("没找到\n");//}//else//{//	printf("找到了\n");//}scpos(&add,pos);

删除pos后面的节点

//删除pos后面的节点
void schpos(SL* pos);


思路:把pos下一个节点给add,   pos->p指向add下一个节点。释放add空间。

//删除pos后面的节点
void schpos(SL* pos)
{assert(pos&&pos->p);//把pos下一个节点给addSL* add = pos->p;//把add下个节点给pospos->p = add->p;//释放addfree(add);add = NULL;
}

	SL* add = NULL;//尾插weic(&add, 1);weic(&add, 2);weic(&add, 3);weic(&add, 4);//查询SL* pos = cx(add, 3);//if (pos == NULL)//{//	printf("没找到\n");//}//else//{//	printf("找到了\n");//}scpos(&add,pos);day(add);

销毁

//链表销毁
void xiaoh(SL** r);

思路:add循环释放,tab保存add下一个节点,释放add,在把tab给add,

最后还剩下*r赋值为NULL。

//链表销毁
void xiaoh(SL** r)
{assert(r && *r);//把r节点给addSL* add = *r;SL* tab = NULL;//循环走全部节点while (add){//把add下一个节点给tabtab = add->p;//释放add节点free(add);//把tab给addadd = tab;}//把一开始的*r赋值为NULL*r = NULL;}

释放完后还剩下*r的空间需要赋值为NULL


链表的分类

链表的结构⾮常多样,以下情况组合起来就有8种(2x2x2)链表结构:

链表说明:

虽然有这么多的链表的结构,但是我们实际中最常⽤还是两种结构:单链表和双向带头循环链表

1.⽆头单向⾮循环链表:结构简单,⼀般不会单独⽤来存数据。实际中更多是作为其他数据结构的⼦ 结构,如哈希桶、图的邻接表等等。另外这种结构在笔试⾯试中出现很多。 

2.带头双向循环链表:结构最复杂,⼀般⽤在单独存储数据。实际中使⽤的链表数据结构,都是带头 双向循环链表。另外这个结构虽然结构复杂,但是使⽤代码实现以后会发现结构会带来很多优势,实 现反⽽简单了,后⾯我们代码实现了就知道了。

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

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

相关文章

RK3568笔记四十二:OLED 屏幕驱动(模拟I2C)

若该文为原创文章&#xff0c;转载请注明原文出处。 本篇记录使用GPIO模拟I2C驱动OLED屏幕&#xff0c;显示界面效果如下。 主要流程是&#xff0c;修改设备树&#xff0c;使用普通IO口&#xff0c;驱动模拟I2C方式&#xff0c;应用程直接传输数据控制。 1、修改设备 2、编写…

LeetCode刷题记录(第三天)55. 跳跃游戏

题目&#xff1a; 55. 跳跃游戏 标签&#xff1a;贪心 数组 动态规划 题目信息&#xff1a; 思路一&#xff1a;动态规划 确定dp数组含义&#xff1a; dp[i] 第[i]个位置能否达到确定递推公式&#xff1a; dp[i] 能不能达到&#xff0c;取决于前面d[i-j]&#xff0c;d[i-j…

Docker无法拉取镜像!如何解决?

问题现象 继去年Docker Hub被xxx后&#xff0c;各大NAS的注册表均出现问题&#xff0c;例如群晖的Docker套件注册表无法连接&#xff08;更新至DSM7.2版本后恢复&#xff09;。而在今年2024年6月初&#xff08;约2024.06.06&#xff09;&#xff0c;NAS中最重要的工具Docker又…

常用设计模式总结

设计模式是在软件开发过程中经常遇到的问题的通用解决方案。它们是经过无数的验证和经验积累的最佳实践。 首先&#xff0c;设计模式是一些前人经验的一些总结&#xff0c;所以&#xff0c;当遇到相似的问题的时候&#xff0c;我们可以直接借鉴好的设计模式来实现&#xff0c;…

02 JDBC

文章目录 JDBC1、JDBC概述2、JDBC快速入门3、JDBC API详解4、数据库连接池5、案例代码 JDBC 1、JDBC概述 JDBC概念 &#xff08;1&#xff09;JDBC 就是使用Java语言操作关系型数据库的一套API &#xff08;2&#xff09;全称&#xff1a;( Java DataBase Connectivity ) Java…

docer笔记3

docker笔记3 容器基本命令 容器基本命令 下载镜像 docker pull cento新建容器并启动 docker run [可选参数] image# 参数说明 --name“Name” 容器名字 tomcat01 tomcat02 用来区分容器 -d 后台方式运行 -it 使用交互方式运行&#xff0c;进入容器查…

R-CNN 中的区域建议网络

区域建议网络&#xff08;Region Proposal Network&#xff0c;RPN&#xff09;是R-CNN&#xff08;Regions with Convolutional Neural Networks&#xff09;架构中的一个关键组件&#xff0c;特别是在Faster R-CNN中。RPN的主要任务是生成可能包含物体的区域提议&#xff0c;…

什么是机器学习以及机器学习如今的社会现状!!

&#x1f468;‍&#x1f4bb;个人主页&#xff1a;开发者-曼亿点 &#x1f468;‍&#x1f4bb; hallo 欢迎 点赞&#x1f44d; 收藏⭐ 留言&#x1f4dd; 加关注✅! &#x1f468;‍&#x1f4bb; 本文由 曼亿点 原创 &#x1f468;‍&#x1f4bb; 收录于专栏&#xff1a…

用太空办公桌spacedesk把废旧平板利用起来

正文共&#xff1a;1500 字 15 图&#xff0c;预估阅读时间&#xff1a;2 分钟 这些年积攒了不少电子设备&#xff0c;比如我现在手头上还有6部手机、4台电脑、2个平板。手机的话&#xff0c;之前研究过作为Linux服务器来使用&#xff08;使用UserLAnd给华为平板装个Linux系统&…

Web 3.0革新:社交金融与边玩边赚开启用户数据主权时代

目录 Web 3.0与社交商业模式 传统社交平台的问题 去中心化社交创新 Mirror&#xff1a;去中心化内容发布平台 Lens Protocol&#xff1a;去中心化社交图谱 Maskbook&#xff1a;隐私保护的社交方式 Web 3.0与与边玩边赚模式 经济模型解析 新商业模式的探索 Axie Infi…

自然语言处理之RNN实现情感分类

前言 IMDB数据集经过分词处理后需要进行额外的预处理&#xff0c;包括将Token转换为index id&#xff0c;并统一文本序列长度。使用MindSpore.dataset接口进行预处理操作&#xff0c;包括text.Lookup和PadEnd接口。此外&#xff0c;还需要将label数据转换为float32格式。 模型…

Android中Activity生命周期详解

目录 一 典型情况二 异常情况2.1 系统配置改变2.2 系统资源不足kill掉低优先级activity Activity是四大组件之一&#xff0c;也是接触的最多的&#xff0c;一般来说Activity经常是与用户交互的界面。 一 典型情况 先看下google官网&#xff0c;其实已经很清楚了 再来个总结 …

mac docker no space left on device

mac 上 docker 拉取镜像报错 Error response from daemon: write /var/lib/docker/tmp/docker-export-3995807640/b8464f52498789c4ebbc063d508f04e8d2586567fbffa475e3cd9afd3c5a7cf2/layer.tar: no space left on device解决&#xff1a; 增加 docker 虚拟磁盘大小。如下图

(C语言) 文件读写基础

文章目录 &#x1f5c2;️前言&#x1f4c4;ref&#x1f4c4;访问标记&#x1f5c3;️文件访问标记 &#x1f5c2;️Code&#x1f4c4;demo&#x1f4c4;分点讲解&#x1f5c3;️打开/关闭&#x1f5c3;️写&#x1f5c3;️读 &#x1f5c2;️END&#x1f31f;关注我 &#x1f…

自学Java第11Day

学习目标&#xff1a;面向对象进阶 学习内容&#xff1a;包、final、权限修饰符、代码块、抽象类、接口 学习时间&#xff1a; 下午 3 点-下午 6 点 学习产出&#xff1a; 什么的包&#xff1f; 包就是文件夹。用来管理各种不同功能的Java类&#xff0c;方便后期代码维护。 包…

pycharm关闭项目时,页面卡住了,怎么办?

问题 在关闭pycharm时&#xff0c;有时会遇到卡在退出进度条的界面&#xff0c;很讨厌&#xff0c;那我们要怎么办才能退出呢&#xff1f; 说明&#xff1a;本篇文章不是从根源上解决这个问题&#xff0c;无法避免这种情况。 解决方法 方法一&#xff1a; 在卡住时&#xf…

python实现图像特征提取算法1

python实现Marr-Hildreth算法、Canny边缘检测器算法 1.Marr-Hildreth算法详解算法步骤公式Python 实现详细解释优缺点 2.Canny边缘检测器算法详解算法步骤公式Python 实现详细解释优缺点 1.Marr-Hildreth算法详解 Marr-Hildreth算法是一个用于图像边缘检测的经典算法&#xff…

思维+01背包,LeetCode LCP 47. 入场安检

一、题目 1、题目描述 「力扣挑战赛」 的入场仪式马上就要开始了&#xff0c;由于安保工作的需要&#xff0c;设置了可容纳人数总和为 M 的 N 个安检室&#xff0c;capacities[i] 记录第 i 个安检室可容纳人数。安检室拥有两种类型&#xff1a; 先进先出&#xff1a;在安检室中…

Git笔记:(上传Git、Git获取文件版本、删除、统计)

目录 一、上传文件到github 1.1 配置用户名和邮箱 1.2 初始化本地仓库 1.3 添加项目目录下所有文件至本地仓库 1.4 使用如下命令加入注释提交 1.5 将本地仓库与远程仓库相连接 1.6 将本地仓库中的文件推送至指定的远程仓库中 二、git获取不同版本 2.1 git下载特定历史…

【SpringBoot3】全局异常处理

【SpringBoot3】全局异常处理 一、全局异常处理器step1&#xff1a;创建收入数字的页面step2:创建控制器&#xff0c;计算两个整数相除step3:创建自定义异常处理器step5&#xff1a;创建给用提示的页面step6&#xff1a;测试输入&#xff08;10/0&#xff09; 二、BeanValidato…