当前位置: 首页 > news >正文

初阶数据结构【3】--单链表(比顺序表还好的一种数据结构!!!)

本章概述

  • 前情回顾
  • 单链表
  • 实现单链表
  • 彩蛋时刻!!!

前情回顾

咱们在上一章博客点击:《顺序表》的末尾,提出了一个问题,讲出了顺序表的缺点——有点浪费空间。所以,为了解决这个问题,我们今天就要讲解链表,咱们在讲结构体的时候有提到过链表,链表的最大优点——一点空间也不浪费,用多少就开辟多少在这里插入图片描述

单链表

  • 概念一种在逻辑上成线性结构,在物理空间上不一定成线性结构的数据结构因为链表是线性表的一种,所以在逻辑上成线性结构“ 链 ”字上就能猜到,在逻辑上成线性结构)。我们链表的开辟用的内存函数malloc。知识点忘记的同学自行回顾:点击:《动态内存管理》。因为内存开辟是随机的,所以我们也不知道它会在内存的那一块区域开辟空间,这就导致开辟的空间可能是连续的,也可能不是连续的。所以,在物理空间上不一定成线性结构。在这里插入图片描述
  • 节点每一个个独立,而且还能存放数据的空间被称为节点。数据结构是用来存储数据的,链表自然也是来存储数据的。存储数据就需要有空间,自然有malloc来给我们开辟空间。万事俱备,只欠东风!可是有想过一个问题吗?——malloc开辟的空间东一块,西一块的它不像realloc那样开辟的连续空间(我们可以挨着挨着找到空间),它的空间是散乱的,不连续的。那么,我们该怎样进行数据的存储,而且还能找到一个一个的数据呢我们可以在这个节点内部划分两部分,一部分用来存储我们想要存储的数据,另一部分部分用来存储下一个节点的地址(因为我们的节点空间是用nalloc开辟的,所以每个节点自然就有个地址)这就需要用到结构体了,进行链表的结构展示:
typedef int SLDatatype ;
typedef struct  Sqlist
{SLDatatype data;	//存放数据,这里假设存放整型类型struct Sqlist* next;   //存放下一个节点的地址
}SLND;		  //定义节点类型

这样我们就能通过地址找到下一个节点了,以此类推,我们就能顺次找到各个节点,找到数据了。如图所示:在这里插入图片描述
当我们不想再存储数据时,要给最后一个节点的next指针赋值NULL(下一个节点为NULL,就相当于没有下一个节点),就表示到此结束了。

  • 链表的性质
    • 1、链式机构在逻辑上是连续的,在物理结构上不⼀定连续。
    • 2、结点⼀般是从堆上申请的。
    • 3、从堆上申请来的空间,是按照⼀定策略分配出来的,每次申请的空间可能连续,可能不连续。
  • 链表的打印:我们讲过了节点的存储结构了,我们可以通过next指针,找到下一个节点,然后就能打印我们想要的数据了。
  • 直观体验链表:我们先给大家直观感受一下链表,让大家有个感觉,我们就按照上面的结构图进行展示:
#define  _CRT_SECURE_NO_WARNINGS	1
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
typedef int SLDatatype;
typedef struct Sqlist
{SLDatatype data;struct Sqlist* next;
}SLND;void my_printf(SLND* ps)
{assert(ps);while (ps){printf("%d->", ps->data);ps = ps->next;}printf("NULL");
}
void test()
{SLND* plist=NULL;  //创立一个带头指针,用来牵引后面的链表,就像火车头一样SLND* node1 = (SLND*)malloc(sizeof(SLND));   //创立3个节点SLND* node2 = (SLND*)malloc(sizeof(SLND));SLND* node3 = (SLND*)malloc(sizeof(SLND));node1->data = 1;  //分别对3个节点·进行·数据的存储node2->data = 2;node3->data = 3;node1->next = node2;  //每个节点的next的指针指向下一个节点的地址node2->next = node3;node3->next = NULL;plist = node1;my_printf(plist);
}
int main()
{test();return 0;
}

结果运行图:在这里插入图片描述
我们的指针plist就是个牵引的作用,就像高铁一样,没有高铁头车厢照样能跑,就是不美观,没有它也可以正常访问链表。有了它就是逻辑上和美观上要舒服很多。如图所示:在这里插入图片描述

实现单链表

和顺序表一样,我们也是创建3个文件: Sqlist .h , Sqlist.ctest .c文件。具体的原因个顺序表一样的。接下来我直接给大家展示代码,我会在注释中详细讲解的。

  • Sqlist.h:
#pragma once
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
typedef int SListDatatype;
typedef struct SListNode
{SListDatatype data;struct SListNode* next;
}SLND;SLND* SListFind(SLND* phead, SListDatatype x);//找对应的节点
SLND* SLTButnode(SListDatatype x);//申请新的节点
void my_printf(SLND* phead); //·打印链表信息void SListpushback(SLND** pphead, SListDatatype x); //尾插
void SListpopback(SLND** pphead);//尾删void SListpushFront(SLND** pphead, SListDatatype x); //头插
void SListpopFront(SLND** pphead);//头删void SListrdFrontpush(SLND** pphead, SLND* find, SListDatatype x);//在任意节点之前插入数据
void SListrdBackpush(SLND* find, SListDatatype x);//在任意节点之后插入数据void SListposePop(SLND* phead, SLND* pose); //删除指定节点
void SListDestry(SLND** pphead); //销毁链表
  • Sqlist.c:
#include "SList.h"
//打印链表信息
void my_printf(SLND* phead)   //打印信息,便于我们看信息的输出
{SLND* pcur = phead;while (pcur){printf("%d-> ",pcur->data);pcur = pcur->next;}printf("NULL\n"); //第N+1个为空====没有链表
}
SLND * SLTButnode(SListDatatype x)	//申请新空间,返回新空间地址,便于咱们找到新空间插入数据
{SLND* newnode = (SLND*)malloc(sizeof(SLND));if (newnode == NULL){perror("malloc fail!");exit(1);		//若开辟失败就直接退出程序,也可以写成 return 1;}else{newnode->data = x;  	//走到这里就开辟成功,进行初始化newnode->next = NULL;}return newnode;
}
void SListpushback(SLND** pphead, SListDatatype x) //尾插数据
{assert(pphead);  //检查传递的指针是否为空指针SLND* newnode = SLTButnode(x);if (*pphead==NULL)  {*pphead = newnode;	//如果起始没有节点,先创立个节点}else{SLND* ptail = *pphead;while (ptail->next)      //遍历节点,找到最后一个节点的next为空的情况,跳出循环{ptail = ptail->next;  }ptail->next = newnode;  //走到这里说明找到了最后一个节点,在此之后插入新的节点}
}
void SListpopback(SLND** pphead) //尾删数据
{assert(pphead&&*pphead);  //检查传递的指针是否为空指针   SLND* ptail = *pphead;  //我们把起始节点的地址给ptail,用ptail进行遍历数据,去寻找最后的尾节点SLND* prev = NULL;   //prev是用来记录尾节点前一个节点,不记录的话,就会随着我们删除最后一个节点而丢失信息while (ptail->next){prev = ptail;ptail = ptail->next;}prev->next = NULL;free(ptail);  //找到最后一个节点,进行空间释放ptail = NULL;  //释放后要置空,防止产生野指针
}
void SListpushFront(SLND**pphead,SListDatatype x) //头插数据
{assert(pphead);  //判断传递的地址是否为空指针SLND* newnode = SLTButnode(x);newnode->next = *pphead;*pphead = newnode;
}
void SListpopFront(SLND**pphead)//头删
{assert(pphead&&*pphead);	//判断传递的地址是否为空指针SLND*next = (*pphead)->next;free(*pphead);  //释放头节点*pphead = next;  //释放头节点后,要使得plist指向第二个节点
}
SLND* SListFind(SLND*phead,SListDatatype x) //找对应的节点
{assert(phead); 	//判断传递的地址是否为空指针while (phead){if (phead->data == x)return phead;    //找到目标节点,就返回目标节点的地址phead = phead->next;}return NULL;  //没找到就返回空指针
}
void SListrdFrontpush(SLND**pphead,SLND*find,SListDatatype x)//在任意节点之前插入数据
{assert(pphead&&*pphead);		//判断传递的地址是否为空指针if (*pphead == find)    //任意位置刚好是头节点时,相当于头插数据SListpushFront(pphead, x); //头插数据else {SLND*newnode= SLTButnode(x);//申请新的节点SLND* prev = NULL;SLND* ptail = *pphead;while (ptail!= find){prev = ptail;ptail = ptail->next;}prev->next = newnode;  //指定位置之前的节点的next指针指向要插入的新节点newnode->next = ptail;		//这个新节点的next指针指向下一个节点}
}
void SListrdBackpush( SLND* find, SListDatatype x)//在任意节点之后插入数据
{assert(find);		//判断传递的地址是否为空指针SLND* newnode = SLTButnode(x);newnode->next = find->next;  //新节点的next指针指向下一个节点find->next = newnode;		//新节点之前的节点next指针指向这个新节点
}
void SListposePop(SLND**pphead, SLND* pose) //删除指定节点
{assert(*pphead&&pose);		//判断传递的地址是否为空指针if(pose==*pphead)		//当删除的节点是头节点时,就相当于头删SListpopFront(pphead);//头删else{SLND* prev = NULL;	SLND* ptail = *pphead;while (ptail != pose)		//遍历节点,找到目标节点{prev = ptail;ptail = ptail->next;}SLND* next = ptail->next;	prev->next = next;free(ptail);  //删除指定的节点ptail = NULL;    //置空指针,防止发生野指针}
}//链表的销毁和顺序表不一样
void SListDestry(SLND**pphead) //链表的销毁不像顺序表那样直接释放内存就欧克,因为每个
{							//节点都是独立的,所以我们需要去遍历每个节点去销毁assert(*pphead);		//判断传递的地址是否为空指针SLND* prev = NULL;SLND* ptail = *pphead;while (ptail->next) //一直遍历到最后一个节点才结束{prev = ptail;ptail = ptail->next;free(prev); //每遍历一个节点就释放prev = NULL;  //置空指针防止产生野指针}ptail = NULL;		//置空指针防止产生野指针*pphead = NULL;	//置空指针防止产生野指针
}
  • test.h
#define  _CRT_SECURE_NO_WARNINGS	1
#include "SList.h"
void test()
{		//这里给大家演示一下尾插数据,其它功能大家可以自行尝试一下SLND* plist = NULL;SListpushback(&plist,1);//尾插SListpushback(&plist,2);//尾插SListpushback(&plist,3);//尾插SListpushback(&plist,4);//尾插my_printf(plist);
}int main()
{test();return 0;
}

结果运行图:在这里插入图片描述

彩蛋时刻!!!

歌曲:《All Falls Down》听会儿歌曲放松一下呗!!!
在这里插入图片描述
每章一句道路是曲折的,前途是光明的!感谢你能看到这里,点赞+关注+收藏+转发是对我最大的鼓励,咱们下期见!!!

相关文章:

初阶数据结构【3】--单链表(比顺序表还好的一种数据结构!!!)

本章概述 前情回顾单链表实现单链表彩蛋时刻&#xff01;&#xff01;&#xff01; 前情回顾 咱们在上一章博客点击&#xff1a;《顺序表》的末尾&#xff0c;提出了一个问题&#xff0c;讲出了顺序表的缺点——有点浪费空间。所以&#xff0c;为了解决这个问题&#xff0c;我…...

mysql迁移到达梦的修改点

字段是达梦关键字的&#xff0c;达梦会给转成大写&#xff0c;如果不要转则需要使用双引号引起来。关键字参考&#xff1a;D:\dmdbms\doc\DM8_SQL语言使用手册.pdf 例如&#xff1a;RowCount Level Content Password Locked 中文乱码问题&#xff0c;需要在应用程序所在服务器的…...

Go小技巧易错点100例(十八)

正文&#xff1a; 使用下划线增加数字可读性 有时候我们代码里会定义很长的数字&#xff0c;虽然计算机程序能支持很大的数据的计算&#xff0c;但是对我们来说&#xff0c;可读性是一个需要考虑的点&#xff0c;特别是1后面全是0的时候。 但是这个问题在Go语言中是可以通过…...

【python】极简教程8-字符串

第八章:字符串 8.1 字符串即序列 字符串是一系列字符的有序集合,可以使用索引访问字符串中的各个字符,索引从 0 开始。 示例代码: fruit = banana letter = fruit[1] print(letter) # 输出: a8.2 len 函数 len 函数返回字符串的长度(字符数)。...

UEFI EDK2框架学习 (四)——UEFI图形化

一、修改protocol.c #include <Uefi.h> #include <Library/UefiLib.h> #include <Library/UefiBootServicesTableLib.h> #include <stdio.h>EFI_STATUS EFIAPI UefiMain(IN EFI_HANDLE ImageHandle,IN EFI_SYSTEM_TABLE *SystemTable ) {EFI_STATUS S…...

【C++】— 一篇文章让你认识STL

文章目录 &#x1f335;1.什么是STL&#xff1f;&#x1f335;2.STL的版本&#x1f335;3.STL的六大组件&#x1f335;4.STL的重要性&#x1f335;5. 如何学习STL&#x1f335;6. 学习STL的三种境界 &#x1f335;1.什么是STL&#xff1f; STL是Standard Template Library的简称…...

mysql--索引

目录 1、长什么样 2、硬件理解 3、软件理解 4、进一步认识 5、索引的理解 6、为什么不选择其他数据结果&#xff1f; 7、聚簇索引和非聚簇索引 8、索引操作 &#xff08;1&#xff09;主键索引创建 第一种方式 第二种方式 第三种方式 主键索引的特点 &#xff08…...

【linux】线程 (三)

13. 常见锁概念 &#xff08;一&#xff09;了解死锁 死锁是指在一组进程中的各个进程均占有不会释放的资源&#xff0c;但因互相申请被其他进程占有的&#xff0c;且不释放的资源&#xff0c;而处于的一种永久等待状态 &#xff08;二&#xff09;死锁四个必要条件 互斥条件…...

c++日常积累

在 C 中&#xff0c;可以直接将 int 类型的值赋值给 bool 类型。C 会自动进行类型转换&#xff0c;任何非零的 int 值都会被转换为 true&#xff0c;而 0 会被转换为 false。 QDialog 有一个 finished(int) 信号&#xff0c;该信号在对话框关闭时发出&#xff0c;并传递一个整…...

字节流写入文件

一、创建输出流对象表示的文件三种方式 方法一&#xff1a; FileOutputStream fos new FileOutputStream("fos.txt",true);//最简便方法二&#xff1a; FileOutputStream fos new FileOutputStream(new File("fos.txt"));方法三&#xff1b; File f ne…...

Torch模型导入

冻结param的3种方式 for param in net.lstm.parameters():param.requires_grad Truenet.lstm.requires_grad True # 无效net.lstm.state_dict()[weight_ih_l0].requires_gradFalsenet.lstm.weight_ih_l0.requires_grad False# dir(net.lstm) to validate attributes …...

博弈论:博弈类型空间集合;三层博弈拓展式;

目录 博弈论:博弈类型空间集合 θ(Dss-1=1 )就是博弈类型空间集合; 一、博弈的基本要素 二、博弈的主要类型 三、博弈类型空间集合的构建 三层博弈拓展式: 博弈论:博弈类型空间集合 这的博弈类型空间集合:指一方选择的策略,用符号进行表达:SDss-2(θDss-1=1) = …...

数据库表的关联、集合操作

数据库表的关联、集合操作 join、MySQL、Oracle什么left right的老是忘&#xff0c;归根到底还是不熟练&#xff0c;记录下来&#xff0c;以后就不用再搜了。 设表A、表B分别包含员工信息和部门信息。 表A包含员工的ID、姓名和部门ID&#xff0c; 表B包含部门ID和部门名称。 …...

word怎么清除格式,Word一键清除所有格式教程

你是否曾在编辑Word文档时遇到过复制内容时格式混乱的情况?别担心&#xff0c;这只需要清除一下格式就可以了&#xff0c;很多朋友还不知道word怎么清除格式&#xff0c;下面小编就来给大家讲一讲word一键清除所有格式的方法教程&#xff0c;操作非常简单&#xff0c;有需要的…...

ShardingProxy服务端分库分表

目录 一、为什么要有服务端分库分表&#xff1f; 二、ShardingProxy基础使用 1、部署ShardingProxy 2、配置常用分库分表策略 三、ShardingSphere中的分布式事务机制 1、什么是XA事务&#xff1f; 2、实战理解XA事务 3、如何在ShardingProxy中使用另外两种事务管理器&a…...

开源的 FOC(Field-Oriented Control) 项目

开源的 FOC&#xff08;Field-Oriented Control&#xff09; 项目通常用于控制无刷直流电机&#xff08;BLDC&#xff09;和永磁同步电机&#xff08;PMSM&#xff09;。这些项目可以实现高效的电机控制&#xff0c;广泛应用于机器人、无人机、电动车等领域。以下是一些著名的开…...

高等数学 5.5 反常积分的审敛法 Γ函数

文章目录 一、无穷限反常积分的审敛法二、无界函数的反常积分审敛法三、 Γ \Gamma Γ 函数 一、无穷限反常积分的审敛法 定理1 设函数 f ( x ) f(x) f(x) 在区间 [ a , ∞ ) [a, \infty) [a,∞) 上连续&#xff0c;且 f ( x ) ⩾ 0 f(x) \geqslant 0 f(x)⩾0.若函数 F (…...

宝塔安装ffmpeg的方法

宝塔安装ffmpeg的方法 wget http://download.bt.cn/install/ext/ffmpeg.sh && sh ffmpeg.sh安装完后可输入以下命令是否安装成功&#xff1a; ffmpeg -version...

案例分享-优秀蓝色系UI界面赏析

蓝色UI设计界面要提升舒适度&#xff0c;关键在于色彩搭配与对比度。选择柔和的蓝色调作为主色&#xff0c;搭配浅灰或白色作为辅助色&#xff0c;能营造清新、宁静的氛围。同时&#xff0c;确保文字与背景之间有足够的对比度&#xff0c;避免视觉疲劳&#xff0c;提升阅读体验…...

陪诊小程序之uniapp(从入门到精通)

1.uniapp如何使用vue3编写页面 <template><view class"content"><navbar name"navbar组件"></navbar><image class"logo" src"/static/logo.png"></image><view class"text-area"&…...

TDengine 快速体验(Docker 镜像方式)

简介 TDengine 可以通过安装包、Docker 镜像 及云服务快速体验 TDengine 的功能&#xff0c;本节首先介绍如何通过 Docker 快速体验 TDengine&#xff0c;然后介绍如何在 Docker 环境下体验 TDengine 的写入和查询功能。如果你不熟悉 Docker&#xff0c;请使用 安装包的方式快…...

Java 8 Stream API 入门到实践详解

一、告别 for 循环&#xff01; 传统痛点&#xff1a; Java 8 之前&#xff0c;集合操作离不开冗长的 for 循环和匿名类。例如&#xff0c;过滤列表中的偶数&#xff1a; List<Integer> list Arrays.asList(1, 2, 3, 4, 5); List<Integer> evens new ArrayList…...

云启出海,智联未来|阿里云网络「企业出海」系列客户沙龙上海站圆满落地

借阿里云中企出海大会的东风&#xff0c;以**「云启出海&#xff0c;智联未来&#xff5c;打造安全可靠的出海云网络引擎」为主题的阿里云企业出海客户沙龙云网络&安全专场于5.28日下午在上海顺利举办&#xff0c;现场吸引了来自携程、小红书、米哈游、哔哩哔哩、波克城市、…...

FFmpeg 低延迟同屏方案

引言 在实时互动需求激增的当下&#xff0c;无论是在线教育中的师生同屏演示、远程办公的屏幕共享协作&#xff0c;还是游戏直播的画面实时传输&#xff0c;低延迟同屏已成为保障用户体验的核心指标。FFmpeg 作为一款功能强大的多媒体框架&#xff0c;凭借其灵活的编解码、数据…...

【Redis技术进阶之路】「原理分析系列开篇」分析客户端和服务端网络诵信交互实现(服务端执行命令请求的过程 - 初始化服务器)

服务端执行命令请求的过程 【专栏简介】【技术大纲】【专栏目标】【目标人群】1. Redis爱好者与社区成员2. 后端开发和系统架构师3. 计算机专业的本科生及研究生 初始化服务器1. 初始化服务器状态结构初始化RedisServer变量 2. 加载相关系统配置和用户配置参数定制化配置参数案…...

Spring AI 入门:Java 开发者的生成式 AI 实践之路

一、Spring AI 简介 在人工智能技术快速迭代的今天&#xff0c;Spring AI 作为 Spring 生态系统的新生力量&#xff0c;正在成为 Java 开发者拥抱生成式 AI 的最佳选择。该框架通过模块化设计实现了与主流 AI 服务&#xff08;如 OpenAI、Anthropic&#xff09;的无缝对接&…...

NLP学习路线图(二十三):长短期记忆网络(LSTM)

在自然语言处理(NLP)领域,我们时刻面临着处理序列数据的核心挑战。无论是理解句子的结构、分析文本的情感,还是实现语言的翻译,都需要模型能够捕捉词语之间依时序产生的复杂依赖关系。传统的神经网络结构在处理这种序列依赖时显得力不从心,而循环神经网络(RNN) 曾被视为…...

自然语言处理——Transformer

自然语言处理——Transformer 自注意力机制多头注意力机制Transformer 虽然循环神经网络可以对具有序列特性的数据非常有效&#xff0c;它能挖掘数据中的时序信息以及语义信息&#xff0c;但是它有一个很大的缺陷——很难并行化。 我们可以考虑用CNN来替代RNN&#xff0c;但是…...

Linux --进程控制

本文从以下五个方面来初步认识进程控制&#xff1a; 目录 进程创建 进程终止 进程等待 进程替换 模拟实现一个微型shell 进程创建 在Linux系统中我们可以在一个进程使用系统调用fork()来创建子进程&#xff0c;创建出来的进程就是子进程&#xff0c;原来的进程为父进程。…...

python报错No module named ‘tensorflow.keras‘

是由于不同版本的tensorflow下的keras所在的路径不同&#xff0c;结合所安装的tensorflow的目录结构修改from语句即可。 原语句&#xff1a; from tensorflow.keras.layers import Conv1D, MaxPooling1D, LSTM, Dense 修改后&#xff1a; from tensorflow.python.keras.lay…...