数据结构(初阶)(三)----单链表
单链表
概念
概念:链表是⼀种物理存储结构上⾮连续、⾮顺序的存储结构,数据元素的逻辑顺序是通过链表中的指针链接次序实现的。
结点
与顺序表不同的是,链表的结构类似于带车头的火车车厢,,链表的每个车厢都是独立申请下来的空间,每个车厢被叫做结点。
结点由当前结点要保存的数据和保存下一个节点的地址(指针变量)。
图中指针变量 plist保存的是第⼀个结点的地址,我们称plist此时“指向”第⼀个结点,如果我们希望plist“指向”第⼆个结点时,只需要修改plist保存的内容为0x0012FFc8
链表中每个结点都是独⽴申请的(即需要插⼊数据时才去申请⼀块结点的空间),我们需要通过指针 变量来保存下⼀个结点位置才能从当前结点找到下⼀个结点
链表的性质
链表在逻辑上是连续的,在物理结构上不一定连续
结点一般是从堆上申请的
从堆上申请来的空间,是按照⼀定策略分配出来的,每次申请的空间可能连续,可能不连续
定义链表的结构实际上就是定义链表中结点结构
每个结点对应的结构体代码:
typedef int SLTDataType;//因为我们在事先并不确定数据的类型,所以需要定义一下
typedef truct SListNode
{SLTDataType data;//结点要保存的数据struct SListNode* next;//指向下一个结点的指针,定义的数据类型是结点类型
}SLTNode;//定义全局变量
当我们想要保存一个数据时,实际上是向操作系统申请一块内存,这块内存不仅要保存数据,也要保存指向下一个结点的地址(当下一个结点为NULL时,地址为NULL)
当我们想要从第一个节点走向最后一个结点时,只需要在当前结点拿上下⼀个结点的地址就可以了。
链表的打印
void SLTPrint(SLTNode* phead)
{SLTNode* pcur = phead;//pcur存储的是当前的结点while (pcur)//等价于pcur != NULL{printf("%d-> ",pcur->data);pcur = pcur->next;}//走到这里,说明pcur == NULL,那么直接打印printf("NULL\n");
}
单链表的实现
创建三个文件分别是SList.h和SList.c和test.c
SList.h
用来包含所需头文件,声明函数
SList.c
用来定义函数,实现方法
test.c
用来测试我们想要实现的功能
这里使用的是VS2022 win11环境
手动构造链表
SList.h
#pragma once
#include<stdio.h>
#include<stdlib.h>typedef int SLTDataType;//定义数据类型
typedef struct SListNode
{SLTDataType data;//定义下一个结点的地址,类型是struct SListNodestruct SListNode* next;
}SLTNode;//定义全局变量//打印链表
void SListPrint(SLTNode* phead);
SList.c
#define _CRT_SECURE_NO_WARNINGS
#include"SList.h"//打印链表
void SListPrint(SLTNode* phead)
{//创建pcur存储phead的地址,直接用phead也可以//这里是因为再次使用第一个结点时,还可以找到,不会影响后续的使用SLTNode* pcur = phead;//判断当前结点是否为NULL,等价于pcur != NULLwhile (pcur){//结点不为NULL,打印当前节点数据printf("%d -> ",pcur->data);//将下一个结点的地址赋值给pcurpcur = pcur->next;}//走到这里说明pcur == NULL,直接打印NULLprintf("NULL\n");
}
test.c
#define _CRT_SECURE_NO_WARNINGS
#include"SList.h"//手动构造链表
void test1()
{//为结点申请内存,大小为结构体大小//node1来接收,类型为SLTNode*,其他同理SLTNode* node1 = (SLTNode*)malloc(sizeof(SLTNode));SLTNode* node2 = (SLTNode*)malloc(sizeof(SLTNode));SLTNode* node3 = (SLTNode*)malloc(sizeof(SLTNode));SLTNode* node4 = (SLTNode*)malloc(sizeof(SLTNode));//在结点中放入要存储的数据node1->data = 1;node2->data = 2;node3->data = 3;node4->data = 4;//在结点中放入指向下一个结点的地址,如果是尾结点,则为NULLnode1->next = node2;node2->next = node3;node3->next = node4;node4->next = NULL;//打印链表看看效果//将首结点的地址给plist,传给实现打印的函数SLTNode* plist = node1;SListPrint(plist);
}int main()
{test1();return 0;
}
单链表实现
尾插
//向操作系统申请一个新结点
SLTNode* SLTBuyNode(SLTDataType x)
{SLTNode* newnode = (SLTNode*)malloc(sizeof(SLTNode));if (newnode == NULL){perror("malloc");exit(1);}//成功newnode->data = x;newnode->next = NULL;return newnode;
}//尾插
//其实在链表为NULL的情况下,不需要再改变头结点的内容,所以也可以使用SLTNode* pphead
void SLTPushBack(SLTNode** pphead, SLTDataType x)
{//向操作系统申请一个新结点SLTNode* newnode = SLTBuyNode(x);//链表为空,phead直接指向newNodeif (*pphead == NULL){*pphead = newnode;}//链表不为空,找尾节点,将尾节点和新节点连接起来else{SLTNode* ptail = *pphead;while (ptail->next != NULL){ptail = ptail->next;}//此时有:ptail->next == NULLptail->next = newnode;}
}
尾删
普遍情况是有多个结点,思路是:
1,找到尾结点
2,将尾结点的前驱结点置为空,再将尾结点释放并置空
特殊情况是,只有一个结点,思路是:
将唯一一个结点,也就是头结点释放并置空
//尾删
void SLTPopBack(SLTNode** phead)
{//二级指针不为空,链表不为空assert(phead && *phead);//在只有一个结点的情况下,prev = NULL,所以在下面对空指针的操作就是不合法的,//所以此时要分离出来,特殊处理,之后我们再测试assert断言就发挥了作用//注意*的优先级是低于->的,所以需要加上括号if ((*phead)->next == NULL){free(*phead);*phead = NULL;}//此时是结点个数大于一的情况else {SLTNode* prev = NULL;SLTNode* ptail = *phead;while (ptail->next){prev = ptail;ptail = ptail->next;}prev->next = NULL;free(ptail);ptail = NULL;}
}
头插
//头插
void SLTPushFront(SLTNode** pphead, SLTDataType x)
{SLTNode* newnode = SLTBuyNode(x);newnode->next = *pphead;*pphead = newnode;
}
头删
头删时,只需要将头结点的next结点保存起来,然后将原来的头结点释放,最后将next结点变成新的头结点即可。
//头删
void SLTPopFront(SLTNode** phead)
{assert(phead && *phead);//经过分析,在只有一个结点和多个结点的情况时,都是符合预期的,所以不必分离讨论SLTNode* next = (*phead)->next;free(*phead);*phead = next;}
查找
如果只是在链表中查找数据,那么就不会修改,只需传一级指针和要查找的数据,
而返回值则是所查找到的结点
//查找
SLTNode* SLTFind(SLTNode* phead, SLTDataType x)
{//创建pcur来遍历链表SLTNode* pcur = phead;//当结点不为空,进入循环while (pcur){if (pcur->data == x){return pcur;}pcur = pcur->next;}//没有找到return NULL;
}
指定位置pos
之前插入
既然是插入数据,那么就要对链表进行修改,需要传址调用,要传二级指针,结点pos
,和数据x
//指定位置pos之前插入
void SLTInsert(SLTNode** pphead, SLTNode* pos, SLTDataType x)
{assert(pphead && pos);//如果pos的位置就是头结点,那么就相当于头插if (pos == *pphead){//头插SLTPushFront(pphead, x);}else{//创建新结点SLTNode* newnode = SLTBuyNode(x);//创建prev记录pos的前驱结点SLTNode* prev = *pphead;while (prev->next != pos){prev = prev->next;}//此时找到了posnewnode->next = pos;prev->next = newnode;}
}
指定位置之后pos
插入
//指定位置之后pos插入
void SLTInsertAfter(SLTNode* pos, SLTDataType x)
{//在pos之后插入,就不需要知道头结点SLTNode* newnode = SLTBuyNode(x);newnode->next = pos->next;pos->next = newnode;
}
指定位置删除
//指定位置删除
void SLTErase(SLTNode** pphead, SLTNode* pos)
{assert(pphead && pos);if (pos == *pphead){//头删SLTPopFront(pphead);}else {SLTNode* del = *pphead;while (del->next != pos){del = del->next;}del->next = pos->next;free(pos);pos == NULL;}
}
指定位置之后删除
//指定位置之后删除
void SLTEraseAfter(SLTNode* pos)
{assert(pos);SLTNode* del = pos->next;pos->next = del->next;
}
销毁链表
//销毁链表
void SListDestroy(SLTNode** pphead)
{assert(pphead);SLTNode* pcur = *pphead;while (pcur){SLTNode* next = pcur->next;free(pcur);pcur = next;}*pphead = NULL;
}
链表的分类
相关文章:

数据结构(初阶)(三)----单链表
单链表 概念 概念:链表是⼀种物理存储结构上⾮连续、⾮顺序的存储结构,数据元素的逻辑顺序是通过链表中的指针链接次序实现的。 结点 与顺序表不同的是,链表的结构类似于带车头的火车车厢,,链表的每个车厢都是独立…...
ChatGPT与DeepSeek:AI语言模型的巅峰对决
目录 引言 一、ChatGPT 与 DeepSeek 简介 (一)ChatGPT (二)DeepSeek 二、技术原理剖析 (一)ChatGPT 技术原理 (二)DeepSeek 技术原理 (三)技术原理对比…...

DaoCloud 亮相 2025 GDC丨开源赋能 AI 更多可能
2025 年 2 月 21 日至 23 日,上海徐汇西岸,2025 全球开发者先锋大会以 “模塑全球,无限可能” 的主题,围绕云计算、机器人、元宇宙等多元领域,探讨前沿技术创新、应用场景拓展和产业生态赋能,各类专业论坛、…...

人工智能之数学基础:线性代数中矩阵的运算
本文重点 矩阵的运算在解决线性方程组、描述线性变换等方面发挥着至关重要的作用。通过对矩阵进行各种运算,可以简化问题、揭示问题的本质特征。在实际应用中,我们可以利用矩阵运算来处理图像变换、数据分析、电路网络等问题。深入理解和掌握矩阵的运算,对于学习线性代数以…...

(上)基于机器学习的图像识别——遥感图像分类(LeNet-5;AlexNet;VGGNet;GoogLeNet;ResNet)
遥感图像识别: 专业词汇: kernel:卷积 目录 遥感图像分类 1.1 LeNet-5 视频来源: 任务:使用什么网络实现遥感图像的分类 LeNet-5结构: 遥感图像分类 1.2 AlexNet(冠军) 视频…...

数据集笔记:NUSMods API
1 介绍 NUSMods API 包含用于渲染 NUSMods 的数据。这些数据包括新加坡国立大学(NUS)提供的课程以及课程表的信息,还包括上课地点的详细信息。 可以使用并实验这些数据,它们是从教务处提供的官方 API 中提取的。 该 API 由静态的…...
HTML元素,标签到底指的哪块部分?单双标签何时使用?
1. 标签(Tag) vs 元素(Element) 标签(Tag) 标签是 HTML 中用于定义元素的符号,用尖括号 < > 包裹。例如 <img> 是标签。元素(Element) 元素是由 标签 内容…...
基于ai技术的视频生成工具
一、通用型AI视频生成工具 腾讯智影 特点:支持数字人播报、文字转视频,提供免费模板和素材库,登录即送5分钟免费时长,每日签到可兑换额外额度。 限制:免费版分辨率较低,部分高级功能需付费。 LunaAI.vid…...
【Java 后端】Restful API 接口
Restful API 接口 REST:Representational State Transfer,表现层(前端的视图页面和后端的控制层)资源状态转移。 一种软件架构的风格(格式) RESTful 是目前最流行的互联网软件架构,如果一个架…...

Matlab地图绘制教程第2期—水陆填充图
上一期分享了海岸线图的绘制方法: 本着由浅入深的理念,本期再来分享一下水陆填充图的绘制方法。 先来看一下成品效果: 特别提示:Matlab地图绘制教程系列,旨在降低大家使用Matlab进行地图类科研绘图的门槛,…...

企业知识库搭建:14款开源与免费系统选择
本文介绍了以下14 款知识库管理系统:1.Worktile;2.PingCode;3.石墨文档; 4. 语雀; 5. 有道云笔记; 6. Bitrix24; 7. Logseq等。 在如今的数字化时代,企业和团队面临着越来越多的信息…...

【Linux系统】—— 冯诺依曼体系结构与操作系统初理解
【Linux系统】—— 冯诺依曼体系结构与操作系统初理解 1 冯诺依曼体系结构1.1 基本概念理解1.2 CPU只和内存打交道1.3 为什么冯诺依曼是这种结构1.4 理解数据流动 2 操作系统2.1 什么是操作系统2.2 设计OS的目的2.3 操作系统小知识点2.4 如何理解"管理"2.5 系统调用和…...
Android内存优化指南:从数据结构到5R法则的全面策略
目录 一、APP 内存限制 二、内存的三大问题 2.1、内存抖动(Memory Churn) 2.1.1 频繁创建短生命周期对象 2.1.2 系统API或第三方库的不合理使用 2.1.3 Handler使用不当 2.2、内存泄漏(Memory Leak) 2.2.1 静态变量持有Activity或Context引用 2.2.2 未取消的回调或…...

机器学习:线性回归,梯度下降,多元线性回归
线性回归模型 (Linear Regression Model) 梯度下降算法 (Gradient Descent Algorithm) 的数学公式 多元线性回归(Multiple Linear Regression)...
Linux上用C++和GCC开发程序实现两个不同MySQL实例下单个Schema稳定高效的数据迁移到其它MySQL实例
设计一个在Linux上运行的GCC C程序,同时连接三个不同的MySQL实例,其中两个实例中分别有两个Schema的表结构分别与第三实例中两个Schema个结构完全相同,同时复制两个实例中两个Schema里的所有表的数据到第三个实例中两个Schema里,使…...

RabbitMQ系列(一)架构解析
RabbitMQ 架构解析 RabbitMQ 是一个基于 AMQP 协议的开源消息中间件,其核心架构通过多组件协作实现高效、可靠的消息传递。以下是其核心组件与协作流程的详细说明: 一、核心组件与功能 Broker(消息代理服务器) RabbitMQ 服务端核…...
XSL 语言:XML 样式表的语言基础与应用
XSL 语言:XML 样式表的语言基础与应用 引言 XSL(Extensible Stylesheet Language)是一种专门用于XML文档样式的语言,它允许用户定义XML文档的格式、布局和外观。XSL是XML技术家族中的重要组成部分,与XML和XPATH等语言共同构成了处理和格式化XML文档的强大工具集。本文将…...
【计算机网络】常见tcp/udp对应的应用层协议,端口
TCP 和 UDP 对应的常见应用层协议 📌 基于 TCP 的应用层协议 协议全称用途默认端口HTTPHyperText Transfer Protocol超文本传输协议80HTTPSHTTP Secure加密的超文本传输协议443FTPFile Transfer Protocol文件传输协议(20 传输数据,21 控制连…...

ExpMoveFreeHandles函数分析和备用空闲表的关系
第一部分:ExpMoveFreeHandles和备用空闲表的关系 ULONG ExpMoveFreeHandles ( IN PHANDLE_TABLE HandleTable ) { ULONG OldValue, NewValue; ULONG Index, OldIndex, NewIndex, FreeSize; PHANDLE_TABLE_ENTRY Entry, FirstEntry; EXHAND…...

微服务学习(1):RabbitMQ的安装与简单应用
目录 RabbitMQ是什么 为什么要使用RabbitMQ RabbitMQ的安装 RabbitMQ架构及其对应概念 队列的主要作用 交换机的主要作用 RabbitMQ的应用 通过控制面板操作(实现收发消息) RabbitMQ是什么 RabbitMQ是一个开源的消息队列软件(消息代理…...
进程地址空间(比特课总结)
一、进程地址空间 1. 环境变量 1 )⽤户级环境变量与系统级环境变量 全局属性:环境变量具有全局属性,会被⼦进程继承。例如当bash启动⼦进程时,环 境变量会⾃动传递给⼦进程。 本地变量限制:本地变量只在当前进程(ba…...

Unity3D中Gfx.WaitForPresent优化方案
前言 在Unity中,Gfx.WaitForPresent占用CPU过高通常表示主线程在等待GPU完成渲染(即CPU被阻塞),这表明存在GPU瓶颈或垂直同步/帧率设置问题。以下是系统的优化方案: 对惹,这里有一个游戏开发交流小组&…...
Auto-Coder使用GPT-4o完成:在用TabPFN这个模型构建一个预测未来3天涨跌的分类任务
通过akshare库,获取股票数据,并生成TabPFN这个模型 可以识别、处理的格式,写一个完整的预处理示例,并构建一个预测未来 3 天股价涨跌的分类任务 用TabPFN这个模型构建一个预测未来 3 天股价涨跌的分类任务,进行预测并输…...

蓝桥杯3498 01串的熵
问题描述 对于一个长度为 23333333的 01 串, 如果其信息熵为 11625907.5798, 且 0 出现次数比 1 少, 那么这个 01 串中 0 出现了多少次? #include<iostream> #include<cmath> using namespace std;int n 23333333;int main() {//枚举 0 出现的次数//因…...

什么是Ansible Jinja2
理解 Ansible Jinja2 模板 Ansible 是一款功能强大的开源自动化工具,可让您无缝地管理和配置系统。Ansible 的一大亮点是它使用 Jinja2 模板,允许您根据变量数据动态生成文件、配置设置和脚本。本文将向您介绍 Ansible 中的 Jinja2 模板,并通…...
省略号和可变参数模板
本文主要介绍如何展开可变参数的参数包 1.C语言的va_list展开可变参数 #include <iostream> #include <cstdarg>void printNumbers(int count, ...) {// 声明va_list类型的变量va_list args;// 使用va_start将可变参数写入变量argsva_start(args, count);for (in…...

ubuntu中安装conda的后遗症
缘由: 在编译rk3588的sdk时,遇到编译buildroot失败,提示如下: 提示缺失expect,但是实测相关工具是在的,如下显示: 然后查找借助各个ai工具,重新安装相关的工具,依然无解。 解决&am…...
【java】【服务器】线程上下文丢失 是指什么
目录 ■前言 ■正文开始 线程上下文的核心组成部分 为什么会出现上下文丢失? 直观示例说明 为什么上下文如此重要? 解决上下文丢失的关键 总结 ■如果我想在servlet中使用线程,代码应该如何实现 推荐方案:使用 ManagedE…...

设计模式域——软件设计模式全集
摘要 软件设计模式是软件工程领域中经过验证的、可复用的解决方案,旨在解决常见的软件设计问题。它们是软件开发经验的总结,能够帮助开发人员在设计阶段快速找到合适的解决方案,提高代码的可维护性、可扩展性和可复用性。设计模式主要分为三…...

HarmonyOS-ArkUI 自定义弹窗
自定义弹窗 自定义弹窗是界面开发中最为常用的一种弹窗写法。在自定义弹窗中, 布局样式完全由您决定,非常灵活。通常会被封装成工具类,以使得APP中所有弹窗具备相同的设计风格。 自定义弹窗具备的能力有 打开弹窗自定义布局,以…...