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

【初阶数据结构】线性表之单链表

文章目录

前言

一、单链表的概念与结构

1.概念

2.结点

3.性质

二、实现单链表

1.结构的定义

2.链表的打印和结点的申请

3.单链表的尾插和头插

4.单链表的尾删和头删

5.单链表的查找

6.指定位置之前插入数据和指定位置之后插入数据

7.删除pos结点和删除pos之后的结点

8.单链表的销毁

    总结


前言

各位好久不见,今天我将给大家更新线性表中的单链表,内容较多,希望大家能耐心地看完这篇博客。话不多说,接下来我详细介绍单链表。


一、单链表的概念与结构

1.概念

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

就比如,在生活中,我们坐火车去景点游玩,而火车就是一节一节的,淡季时车次的车厢会相应减少,旺季时车次的车厢会额外增加几节。只需要将火车里的某节车厢去掉加上,不会影响其他车厢,每节车厢都是独立存在的。

在链表里,每节“车厢”是什么样的呢?

链表大致如图所示,链表是由一个一个结点前后相互连接而组成的。前一个节点通过存储后一个节点的地址来找到后一个节点,接下来细说结点的内容。

2.结点

定义:与顺序表不同的是,链表里的每节"车厢"都是独立申请下来的空间,我们称之为“节点/结点”。
结点有两个组成部分:1.保存的数据    2.保存下一个结点的地址(指针变量)
在上图中指针 变量 plist 保存的是第一个结点的地址,我们称  plist  此时“指向”第一个结点,如果我们希望 plist “指向”第二个结点时,只需要修改 plist 保存的内容为 0x0012FFA0
链表中每个结点都是独立申请的(即需要插入数据时才去申请一块结点的空间),我们需要通过指针变量来保存下一个结点位置才能从当前结点找到下一个结点。 
对单链表中一个结点的结构体的定义:
typedef int SLTDataType;
//定义链表结点的结构
typedef struct SListNode
{SLTDataType data;//存储的数据struct SListNode* next;//下一个结点的地址
}SLTNode;//结点结构体的名字,起方便的作用

3.性质

1、链式机构在逻辑上是连续的,在物理结构上不一定连续
2、结点一般是从堆上申请的
3、从堆上申请来的空间,链表的节点是malloc来实现;是按照⼀定策略分配出来的,每次申请的空间可能连续,可能不连续
4、当我们想要保存一个整型数据时,实际是向操作系统申请了一块内存,这个内存不仅要保存整型数据,也需要保存下一结点的地址(当下一个结点为空时保存的地址为空)。
5、当我们想要从第一个结点走到最后一个结点时,只需要在当前结点拿上下一个结点的地址就可以了。

二、实现单链表

1.结构的定义

在上面已介绍单链表中结点的结构定义,我们直接实现即可:

typedef int SLTDataType;
typedef struct SListNode
{SLTDataType data;struct SListNode* next;
}SLTNode;

2.链表的打印和结点的申请

链表的打印:

写打印的函数时,函数名取为SLTPrint,创建一个指针变量pcur,让其从链表的头结点开始遍历,可避免指针在指向下个一个结点的时候把头结点给丢失。

void SLTPrint(SLTNode* phead)
{SLTNode* pcur = phead;while (pcur!=NULL){printf("%d ->", pcur->data);pcur = pcur->next;//指向下一个结点}printf("NULL\n");//表示最后一个结点的下一个结点为NULL
}

结点的申请:

在顺序表中,空间满就需要进行扩容,而在链表中结点的申请也是这种类似的效果。因此,我们需要对链表进行一个结点一个结点的增加来进行扩容。

在写结点的申请的函数时,函数名取为SLTBuyNode (向操作系统买下来一个结点进行扩容), 在上面单链表的性质中,我们可知创建新结点需要用malloc来实现,如果新结点==NULL 就申请失败就退出,反之申请成功就让存储的数据 (node->data) 为x,下一个结点的地址(node->next)为NULL,最后直接返回node即可。

SLTNode* SLTBuyNode(SLTDataType x)
{SLTNode* node = (SLTNode*)malloc(sizeof(SLTNode));if (node == NULL){perror("malloc fail!\n");exit(1);}node->data = x;node->next = NULL;return node;
}

3.单链表的尾插和头插

尾插:

尾插是在最后一个结点的后面插入一个有效数据。先在插入数据之前先向空间申请一个结点大小调用SLTBuyNode()即可。再判断链表是否为空,为空就直接插入即头结点就是新结点;不为空就创建一个变量指针 pcur 从头开始遍历,走到尾结点就让尾结点的下一个结点指向新结点(pcur->next=newnode)。

void SLTPushBack(SLTNode** pphead, SLTDataType x)
{assert(pphead);SLTNode* newnode = SLTBuyNode(x);if (*pphead == NULL){*pphead = newnode;}else {//当链表是非空,找尾结点SLTNode* pcur = *pphead;while (pcur->next!=NULL){pcur = pcur->next;}pcur->next = newnode;}}

头插:

头插是在第一个结点前面插入一个数据,使数据指向第一个结点从而变成新的头结点。先操作系统申请一个结点调用SLTBuyNode()即可,先让新结点的下一个结点指向原来的头结点,这样原来的头结点直接变成新结点所以让新结点变成头结点即可。

void SLTPushFront(SLTNode** pphead, SLTDataType x)
{assert(pphead);//断言一下,避免链表为空的情况下进行操作SLTNode* newnode = SLTBuyNode(x);newnode->next = *pphead;*pphead = newnode;
}

4.单链表的尾删和头删

尾删:

尾删是在一串结点里把最后一个结点进行删除(释放),并使倒数第二个结点指向NULL。先判断链表有一个结点和多个结点的情况:有一个结点时,直接对头结点进行释放再置为NULL;有多结点时,创建两个指针,一个 prev 用来保存尾结点的前一个结点,一个 ptail 从头开始遍历走到尾结点,对尾结点进行释放再置为NULL。

void SLTPopBack(SLTNode** pphead)
{assert(pphead&&* pphead);//只有一个结点的情况if ((*pphead)->next == NULL){free(*pphead);*pphead = NULL;}else {//多结点的情况SLTNode* prev = NULL;SLTNode* ptail = *pphead;while (ptail->next){prev = ptail;ptail = ptail->next;}prev->next = NULL;free(ptail);ptail = NULL;}
}

头删:

头删是在一串结点里把先把第二个结点保存下来再对第一个结点进行释放,并置NULL。先创建一个指针next,用next对头结点的下一个结点进行保存,再对头结点直接释放置为NULL,最后让头结点的下一个结点直接变成新的头结点。

void SLTPopFront(SLTNode** pphead)
{assert(pphead&&* pphead);SLTNode* next = (*pphead)->next;free(*pphead);*pphead = next; 
}

5.单链表的查找

创建指针pcur,让pcur从头开始遍历,判断pcur指向的数据与查找的数据是否相同,如果相同就返回当前的节点;如果不相同让pcur指向下一个节点;循环结束完成都没找到则返回NULL。

SLTNode* SLTFind(SLTNode* phead, SLTDataType x)
{SLTNode* pcur = phead;while (pcur != NULL){if (pcur->data == x){return pcur;}pcur = pcur->next;}//未找到return NULL;
}

6.指定位置之前插入数据和指定位置之后插入数据

指定位置之前插入数据:

创建两个指针,一个是prev从头开始遍历,一个是pos指定的位置;先判断头结点是不是指定位置,是就调用头删的函数,不是就让prev从头开始遍历直到prev->next为pos时就停止遍历;再让新节点的next指向pos,避免pos丢失;后让prev->next指向新节点。

void SLTInsert(SLTNode** pphead, SLTNode* pos, SLTDataType x)
{assert(pphead && pos);//当pos就是头结点时,相当于头插if (pos == *pphead){SLTPushFront(pphead, x);//调用头插的函数}else {SLTNode* newnode = SLTBuyNode(x);SLTNode* prev = *pphead;while (prev->next!=pos){prev = prev->next;}newnode->next = pos;prev->next = newnode;}
}

指定位置之后插入数据:

在指定位置之后插入数据之前先创建一个指针pos指定位置,不需要从头开始遍历,直接让新节点的next指向pos的next;再让pos的next指向newnode。

void SLTInsertAfter(SLTNode* pos, SLTDataType x)
{assert(pos);SLTNode* newnode = SLTBuyNode(x);//是在指定位置之后插入,因此是指向pos->nextnewnode->next = pos->next;pos->next = newnode;
}

7.删除pos结点和删除pos之后的结点

删除pos结点:

创建两个指针,一个是prev从头遍历,一个是pos指定位置先判断头结点是不是指定位置,是指定位置就调用头删的函数,不是指定位置就再让prev从头开始遍历;当prev->next等于pos时停止循环,开始对指定位置进行删除;再删除之前先对pos->next进行保留避免丢失,再对pos进行free后置为NULL。

void SLTErase(SLTNode** pphead, SLTNode* pos)
{assert(pphead && pos);if (pos == *pphead){SLTPopFront(pphead);}else {SLTNode* prev = *pphead;while (prev->next != pos){prev = prev->next;}prev->next = pos->next;free(pos);pos = NULL;}
}

删除pos之后的结点:

创建一个指针pos,直接对pos后面的节点进行销毁。先保存pos->next,再对pos进行free后置为NULL。

void SLTEraseAfter(SLTNode* pos)
{assert(pos && pos->next);SLTNode* del = pos->next;pos->next = del->next;free(del);del = NULL;
}

8.单链表的销毁

创建两个指针,一个是pucr从头开始遍历,一个是next用来保存下一个结点。next要走到pcur的前面,pcur用来销毁所在位置的,next是用来保存pcur->next的。

void SListDestroy(SLTNode** pphead)
{SLTNode* pcur = *pphead;while (pcur){SLTNode* next = pcur->next;free(pcur);pcur = next;}*pphead = NULL;//让头结点置为NULL这样才销毁彻底成功
}


总结

非常感谢大家阅读完这篇博客。希望这篇文章能够为您带来一些有价值的信息和启示。如果您有任何问题或建议,欢迎在评论区留言,我们一起交流学习。

相关文章:

【初阶数据结构】线性表之单链表

文章目录 前言 一、单链表的概念与结构 1.概念 2.结点 3.性质 二、实现单链表 1.结构的定义 2.链表的打印和结点的申请 3.单链表的尾插和头插 4.单链表的尾删和头删 5.单链表的查找 6.指定位置之前插入数据和指定位置之后插入数据 7.删除pos结点和删除pos之后的结…...

CentOS7通过yum安装JDK

CentOS7通过yum安装JDK 1、卸载自带的JDK 查看已安装的JDK rpm -qa | grep java删除已安装的JDK yum -y remove java-1.8.0-openjdk*验证是否删除成功 查不到版本信息则已删除成功 java -version2、安装JDK sudo yum install java-1.8.0-openjdk java-1.8.0-openjdk-deve…...

c# 常见的几种取整场景

软件取整,通常指的是在计算机软件中对数值进行取整操作,即将一个浮点数或小数转换为整数,同时确定如何处理小数部分。取整操作在编程和数学计算中非常常见,不同的取整方法适用于不同的场景。 常见的取整方法 向零取整&#xff08…...

数据库回滚:大祸临头时

原文地址 什么是数据库回滚? 数据库技术中,回滚是通过撤销对数据库所做的一项或多项更改,将数据库返回到先前状态的操作。它是维护数据完整性和从错误中恢复的重要机制。 什么时候需要数据库回滚? 数据库回滚在以下几个场景中很…...

【GoLang】两个字符串如何比较大小?以及字典顺序的比较规则

在 Go 语言中,字符串的比较是基于字典顺序进行的。 字典顺序的比较规则: 比较两个字符串从左到右逐个字符的Unicode码点值, 若比较结果不相等则将此结果作为字符串大小的结果, 若比较结果相等则比较下一位, 若其中一个…...

5G学习笔记之SNPN系列之UE入网和远程配置

参考:3GPP 23.501 5.30.2.10 Onboarding of UEs for SNPNs 小小协议搬运工 目录 0. NPN系列 1. 概述 2. SNPN作为ONN 2.1 DCS参与的入网主鉴权 2.2 DCS不参与的入网主鉴权 2.3 UE入网 3. PLMN作为ONN 4. 远程配置 0. NPN系列 1. NPN概述 2. NPN R18 3. 【SNPN系列】…...

C#版OpenCv常用函数大全

OpenCvSharp 是 OpenCV 的NET封装,提供了丰富的图像处理和计算机视觉功能。以下是一些常用函数及其详细说明。 1. 图像读取与显示 Cv2.ImRead 功能:读取图像文件并返回一个 Mat 对象。用法:Mat image Cv2.ImRead("path/to/image.jpg&…...

Spring Boot教程之五十二:CrudRepository 和 JpaRepository 之间的区别

Spring Boot – CrudRepository 和 JpaRepository 之间的区别 Spring Boot建立在 Spring 之上,包含 Spring 的所有功能。由于其快速的生产就绪环境,使开发人员能够直接专注于逻辑,而不必费力配置和设置,因此如今它正成为开发人员…...

蓝桥杯备考:数据结构之栈 和 stack

栈的概念以及栈的实现 栈是一种只允许在一端进行插入和删除的线性表 空栈:没有任何元素 入栈:插入元素 出栈:删除元素 栈本身就是一个线性表,我们可以写一个足够大的数组来实现栈 除此之外,我们还需要变量n来记录…...

solidity基础 -- 映射

在区块链的智能合约开发领域,Solidity 作为以太坊上最主流的编程语言之一,拥有诸多强大特性助力开发者构建复杂且高效的去中心化应用。其中,映射(Mapping)是一个极为关键的数据结构,它为合约中的数据存储与…...

Angular 11课程实践:构建高效单页应用的支持代码

本文还有配套的精品资源,点击获取 简介:Angular 11是Google支持的前端框架,适合构建复杂的单页应用(SPA)。本课程将深入介绍Angular核心特性,如组件化、依赖注入、数据绑定和路由,并且涵盖Ang…...

测试用例颗粒度说明

当我们在编写测试用例时,总是会遇到一个问题:如何确定测试用例的颗粒度?测试用例过于粗糙,可能无法全面覆盖系统的细节;而颗粒度过细,又会导致测试重复、冗余。掌握合适的颗粒度,不仅可以提高测…...

ESP32 IDF VScode出现头文件“无法打开 源 文件 ”,并有红色下划线警告

问题背景: ESP32 IDF VScode出现头文件“无法打开 源 文件 ”,并有红色下划线警告: 解决办法: 在工程里面的.vscode文件夹下,检查是否存在c_cpp_properties.json文件,如果没有可以手动创建添加。如图…...

Windows安装ES单机版设置密码

下载ES ES下载链接 我用的是7.17.26 启动前配置 解压之后打开D:\software\elasticsearch-7.17.26\bin\elasticsearch-env.bat 在elasticsearch-env.bat文件中修改jdk的路径 修改前 修改内容 if defined ES_JAVA_HOME (set JAVA"D:\software\elasticsearch-7.17.26\…...

Linux Docker

Docker 的定义 Docker 是一个开源的容器化平台,它允许开发者将应用程序及其依赖项打包成一个可移植的容器。容器是一种轻量级、独立的运行环境,与传统的虚拟机不同,容器共享主机操作系统的内核,通过隔离的文件系统、进程空间和网…...

MSE学习

MSE简介 媒体源拓展&#xff08;Media Source Extensions&#xff0c;简称 MSE&#xff09;是一个由 W3C 制定的标准&#xff0c;它允许 JavaScript 代码通过 AJAX 请求获取媒体数据&#xff0c;并将其提供给 HTML 的 <video> 或 <audio> 元素进行播放。 MSE特点…...

0-基于蚁群优化和带注意力机制的循环神经网络的新型混合算法用于解决旅行商问题(HAL science)(完)

文章目录 AbstractI INTRODUCTIONII 旅行商问题的正式描述III STATE OF THE ARTIV 使用的混合化技术原理4.1 Principle of ACO4.2具有注意机制的自动编码器模型V 蚁群优化与具有注意机制的神经网络的混合5.1 基本思想5.2 解决步骤5.2.1 模型训练5.2.2 寻找解VI EXPERIMENTS6.1 …...

MIUI显示/隐藏5G开关的方法,信号弱时开启手机Wifi通话方法

5G网速虽快&#xff0c;手机功耗也大。 1.取消MIUI强制的5G&#xff0c;手动设置4G的方法&#xff01; 【小米澎湃OS, Xiaomi HyperOS显示/隐藏5G开关的方法】 1.1.小米MIUI系统升级后&#xff0c;被强制连5G&#xff0c;手动设置开关被隐藏&#xff0c;如下图&#xff1a; 1…...

挑战20天刷完leecode100

2025.1.5 二分查找 1 搜索插入位置 就是简单的二分查找 注意开闭就行 这里有一句话就是nums是升序的 如果他不是严格递增 就是有相同的数字的情况下应该怎么写? int lower_bound(vector<int>& nums, int target) {int left 0, right (int) nums.size() - 1; …...

Java列表示例

示例1&#xff1a;使用ArrayList创建并操作列表 ArrayList是List接口最常用的实现之一&#xff0c;它内部使用数组来存储元素&#xff0c;因此对于随机访问具有很高的效率。但是&#xff0c;当涉及到频繁的插入或删除操作时&#xff0c;它的性能可能会受到影响&#xff0c;因为…...

调用支付宝接口响应40004 SYSTEM_ERROR问题排查

在对接支付宝API的时候&#xff0c;遇到了一些问题&#xff0c;记录一下排查过程。 Body:{"datadigital_fincloud_generalsaas_face_certify_initialize_response":{"msg":"Business Failed","code":"40004","sub_msg…...

ES6从入门到精通:前言

ES6简介 ES6&#xff08;ECMAScript 2015&#xff09;是JavaScript语言的重大更新&#xff0c;引入了许多新特性&#xff0c;包括语法糖、新数据类型、模块化支持等&#xff0c;显著提升了开发效率和代码可维护性。 核心知识点概览 变量声明 let 和 const 取代 var&#xf…...

练习(含atoi的模拟实现,自定义类型等练习)

一、结构体大小的计算及位段 &#xff08;结构体大小计算及位段 详解请看&#xff1a;自定义类型&#xff1a;结构体进阶-CSDN博客&#xff09; 1.在32位系统环境&#xff0c;编译选项为4字节对齐&#xff0c;那么sizeof(A)和sizeof(B)是多少&#xff1f; #pragma pack(4)st…...

DIY|Mac 搭建 ESP-IDF 开发环境及编译小智 AI

前一阵子在百度 AI 开发者大会上&#xff0c;看到基于小智 AI DIY 玩具的演示&#xff0c;感觉有点意思&#xff0c;想着自己也来试试。 如果只是想烧录现成的固件&#xff0c;乐鑫官方除了提供了 Windows 版本的 Flash 下载工具 之外&#xff0c;还提供了基于网页版的 ESP LA…...

自然语言处理——Transformer

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

浅谈不同二分算法的查找情况

二分算法原理比较简单&#xff0c;但是实际的算法模板却有很多&#xff0c;这一切都源于二分查找问题中的复杂情况和二分算法的边界处理&#xff0c;以下是博主对一些二分算法查找的情况分析。 需要说明的是&#xff0c;以下二分算法都是基于有序序列为升序有序的情况&#xf…...

大学生职业发展与就业创业指导教学评价

这里是引用 作为软工2203/2204班的学生&#xff0c;我们非常感谢您在《大学生职业发展与就业创业指导》课程中的悉心教导。这门课程对我们即将面临实习和就业的工科学生来说至关重要&#xff0c;而您认真负责的教学态度&#xff0c;让课程的每一部分都充满了实用价值。 尤其让我…...

LangChain知识库管理后端接口:数据库操作详解—— 构建本地知识库系统的基础《二》

这段 Python 代码是一个完整的 知识库数据库操作模块&#xff0c;用于对本地知识库系统中的知识库进行增删改查&#xff08;CRUD&#xff09;操作。它基于 SQLAlchemy ORM 框架 和一个自定义的装饰器 with_session 实现数据库会话管理。 &#x1f4d8; 一、整体功能概述 该模块…...

【JVM】Java虚拟机(二)——垃圾回收

目录 一、如何判断对象可以回收 &#xff08;一&#xff09;引用计数法 &#xff08;二&#xff09;可达性分析算法 二、垃圾回收算法 &#xff08;一&#xff09;标记清除 &#xff08;二&#xff09;标记整理 &#xff08;三&#xff09;复制 &#xff08;四&#xff…...

关于uniapp展示PDF的解决方案

在 UniApp 的 H5 环境中使用 pdf-vue3 组件可以实现完整的 PDF 预览功能。以下是详细实现步骤和注意事项&#xff1a; 一、安装依赖 安装 pdf-vue3 和 PDF.js 核心库&#xff1a; npm install pdf-vue3 pdfjs-dist二、基本使用示例 <template><view class"con…...