单链表的定义

article/2025/9/20 10:11:33

前言

在前面的文章中,我们系统的介绍了线性表的顺序存储实现——顺序表。紧接着我们要介绍线性表的链式存储实现——链表。而链表中又有许多的链表:

  1. 单链表
  2. 双链表
  3. 循环链表
  4. 静态链表

这一篇文章中,我们先来介绍单链表。

单链表的定义

什么是单链表

要想知道什么是单链表,我们就可以使用我们学习过的顺序表来对比学习(对比学习有助于你的记忆,使用学习过的知识去学习未知的知识,也未尝不是一种好方法)

物理地址可以不相邻

我们都知道顺序表是申请一大块连续的区域,并且使用物理地址相邻的方法来表示逻辑上的相邻。而链表逻辑上相邻的元素在内存中可以是不相邻存放的。

结点存放数据值和下一结点地址

那么如果链表中的元素在内存中不是相邻存放的,那我们如何知道下一个元素的位置呢?于是,就提出了在一个结点中我们同时存入数据元素值和存放下一个元素的结点。

换句话来说可就是,一个柜子里放入我们需要放入的东西(元素的值)并且放入存放下一个元素的柜子序号(存放下一元素结点的地址),如下图所示:

image-20220706221734138

这里说句题外话:赋给指针一个地址,可以理解为指针指向了这个地址。给指针地址,就是让指针指向。(希望这能更有助于你去理解指针的含义)

优点:不要求大片连续空间,改变容量方便

由于指针不需要通过物理地址相邻来表示逻辑相邻,因此也就不要求大片连续的空间。因此改变链表的容量就会变得很方便。

缺点:不可随机存取,要消耗一定空间存放指针

不可随机存取

顺序表可以通过知道元素的大小和开头的元素地址,从而快速的找到所需要位置的元素。但是链表不行,由于链表结点在内存中是分散存储的,所以导致不能直接找到元素,需要通过遍历元素来寻找。

比如我要找a3这个元素,我就需要先进入到a1,从a1存储的a2的地址找到a2。再访问a2通过a2存放的a3的地址找到a3。

消耗一定空间存放指针

由于链表是通过结点中存放的下一结点的地址,来寻找下一结点,从而使各个分散在内存中的结点联系起来。所以一个结点中就必须分为两部分:一部分存放数据据元素,而另一部分存放下一结点的地址。

用代码定义一个单链表

从上述对于单链表的粗略介绍,我们都知道链表中的结点需要有两部分:一部分存放数据元素,一部分存放下一结点的地址。

那么我们就大概知道了如何去定义一个结构体代表一个结点。

struct LNode{				// 定义单链表结点类型ElemType data;			// 每个结点存放一个数据元素struct LNode *next;		// 指针指向下一个结点
};

以上就是一个链表的结点。

如果我们想扩展链表的容量就可以使用之前使用过的malloc()函数。

struct LNode *p = (struct LNode *)malloc(sizeof(struct LNode)),但是到这里有一个问题,就是我们会发现在我们每次去使用我们定义的这个结构体的时候都需要写成struct LNode...,这对于懒癌患者的我们是不被允许的。为此,我们可以使用typedef来给一些关键字“起外号”。

typedef <数据类型> <别名>:给某个数据类型起一个别名,在后面我们可以使用别名。举个例子:

typedef int zhengshu;		// 给int起一个外号叫做zhengshu
int i 等价 zhengshu i			// 此时使用int i可以声明一个整型的变量,使用zhengshu i同样可以typedef int *zhengshuzhizhen;
int *i 等价 zhegnshuzhizhen i

因此我们可以通过typedef struct LNode LNode来简化,不需要再写struct LNode只用写LNode。当然typedef有两种写法:

typedef struct LNode{ElemType data;struct LNode *next;
}LNode, *LinkList;// 等价于
struct LNode{ElemType data;struct LNode *next;
};
typedef struct LNode LNode;			// 强调是一个结点
typedef struct LNode *LinkList;		// 强调是一个单链表

在单链表中,我们要表明单链表是在哪个位置该如何表示?很简单我们只需要有一个指针指向链表的第一个结点,通过这个指针就可以知道链表所在的位置了,这个指针我们就称为头指针。

头指针声明应该是LNode *L,但是由于我们上面已经给LNode起了个外号叫做LinkList,所以直接使用其声明即可LinkList L。可能有小伙伴会觉得这多此一举,但是这可以增加代码的可读性。我们来举例解释。

LNode * GetElem(LinkList L,int i){...
}

我们可以看到上述这段代码,既使用了LNode *也使用了LinkList。因为在GetElem()这个函数中(主要功能使获取某个元素),我们想要强调的是从一个单链表中(LinkList L)获取某个元素,并且返回的是一个结点(LNode *)。

从这里我们就可以看出来,虽然说LNode *LinkList实现的效果是一样的,但是对于直接字面的意思理解LNode是链表的结点的意思,而LinkList字面意思是单链表。这无疑是增加了代码的可读性。

两种实现

不带头结点

我们首先来实现一下不带头结点(第一个结点)的单链表

typedef struct LNode{ElemType data;struct LNode *next;
}LNode, *LinkList;bool InitList(LinkList &L){L = NULL;				// 表示空表,暂时没有任何结点(防止脏数据)return true;
}void test(){LinkList L;				// 声明一个指向单链表的指针// 初始化一个空表InitList(L);
}

代码分析

  1. 首先我们在这里看test()函数,使用LinkList L来声明一个指向单链表的头指针。使用LinkList而不是LNode是想强调这里的L是一个单链表。
  2. 紧接着进入InitList()初始化函数,将L设置为NULL值初始化防止脏数据,并且通过L中的NULL来表示是空表。并且返回true表示成功。这里使用&L引用,是因为我们要将改变的L带回test()函数中的L,如果不使用引用在InitList()改变的L的值将不会对test()中产生影响

image-20220706232727363

判断单链表是否为空

我们在上面说了,我们将L的值设置为NULL来表示空表,那么我们就可以通过L中的值是否为NULL来判断单链表是否为空表

bool Empty(LinkList L){if(L==NULL)return true;elsereturn false;
}// 简化
bool Empty(LinkList L){return (L==NULL);
}
代码分析

我们通过判断L的值是否为NULL来判断单链表是否为空这非常简单,因此不在解释。这里的简化,我来做一个解释,首先a == b这种运算产生的结果为布尔值(true、false),而我们刚好就是需要L==NULL返回true,否则返回false,正好这个比较的结果返回的也是true或者false,因此可以这样简化。

带头结点

带头结点的实现起始跟不带头结点的实现就是相差了一个是创建一个新的结点。

typedef struct LNode{ElemType data;struct LNode *next;
}LNode, *LinkList;bool InitList(LinkList &L){L = (LNode *)malloc(sizeof(LNode));		// 分配一个头结点if(L==NULL)								// 内存不足分配失败return false;L->next = NULL;							// 头结点之后没有结点return true;
}void test(){LinkList L;				// 声明一个指向单链表的指针// 初始化一个空表InitList(L);
}

代码分析

上述的代码我着重分析一下InitList()函数

  1. 首先使用malloc()函数创建一个新的结点并且将头指针L指向新节点,并且为了代码的健壮性,我们做一个判断,若L==NULL,则代表内存中已经没有空间了,所以分配失败
  2. 如果创建成功,则将新节点指向下一节点的指针设置为NULL,返回true

image-20220706232802736

判断单链表是否为空

bool Empty(LinkList L){if(L->next==NULL)return true;elsereturn false;
}

在带头结点的单链表中,我们通过头结点的指针是否为NULL来判断链表是否为空。

不带头结点 vs 带头结点

首先不带头结点的实现,对于第一个数据结点和后续数据节点的处理需要使用不同的代码来处理(处理逻辑不同),反之。总而言之就是带头结点的实现方法是比较推荐的,因为比较方便。

结束语

已同步更新至个人博客:https://www.hibugs.net/index.php/linklistdf/

本人菜鸟一枚,仅分享学习笔记和经验。若有错误欢迎指出!共同学习、共同进步 😃

如果您觉得我的文章对您有所帮助,希望可以点个赞和关注,支持一下!十分感谢~(若您不想也没关系,只要文章能够对您有所帮助就是我最大的动力!)

下一篇文章传送门:正在更新,敬请期待…


http://chatgpt.dhexx.cn/article/syfyhBIg.shtml

相关文章

(c语言)详解单链表

1&#xff1a;什么是单链表。 我们知道顺序表底层原理其实就是一块可以自由控制大小的数组&#xff0c;顺序表可以实现在任何地方进行插入一个数据&#xff0c;如果顺序表的缺点在于如果要在起始位置插入一个数据就要把后面的每一个数据都往后挪&#xff0c;这样会大量消耗我们…

双向链表中插入元素的几种方式

dnode的结构如下&#xff1a;由前驱prior指针、后继next指针以及数据data&#xff0c;现需要在A、B节点中间插入C节点&#xff0c;给出了A的地址&#xff0c;以及C的地址。 1、利用将链表拆分然后插入方式进行&#xff1a; 先将节点C完全插入到B的前面&#xff0c;再将A指向C&…

单链表的使用方法.数据结构(三)[上]

前言 提示&#xff1a;文本为数据解构(三)单链表&#xff1a; 本文具体讲解单链表的具体使用方法 提示&#xff1a;以下是本篇文 系列文章目录 第一章 数据解构(一) 第二章 顺序表的具体使用方法.数据解构(二) 文章目录 前言 系列文章目录 文章目录 一、单链表视图 二、…

Linux设置ssh免密登录

目录 1.在/root目录下输入命令 2.进入.ssh目录 3.将公钥id_rsa.pub写入到一个认证文件夹中 4.开启远程免密登录配置 5.免密远程登录本机 1.在/root目录下输入命令 [rootlocalhost ~]# ssh-keygen -t rsa -P "" Generating public/private rsa key pair. Enter …

Linux SSH 免密登录

Linux SSH 免密登录 本篇我们来 看看 Linux 的免密登录的原理 以及实际操作一番 概述 什么是 Linux SSH 免密登录&#xff0c;我觉得大家应该都 多少听过 或者操作过&#xff0c;那你真的理解整个免密登录的过程吗&#xff1f; Linux SSH 免密登录 就是 可以不输入密码 就可以…

SSH登录和SSH免密登录

在了解ssh的时候产生了概念混淆&#xff0c;发现ssh登录和ssh免密登录是两码事。 可以从目的和过程对比这两个概念&#xff1a; 1.目的 1.1 SSH登录 简单来说就是&#xff1a;建立客户端和服务器之间安全的远程连接&#xff0c;登录远程服务器&#xff0c;以访问文件系统 。…

VSCode——SSH免密登录

文章目录 本地PC端&#xff08;一般为Windows&#xff09;1. 检查自己是否已经生成公钥2. 配置VScode的SSH config 远程服务器端1. 服务器新建授权文件2. 赋权限3. 重启远程服务器的ssh服务 最全步骤&#xff1a;【设置ssh免密不起作用&#xff1f;彻底搞懂密钥】vscode在remot…

SSH免密登录功能配置

文章目录 一、SSH免密登录功能配置&#xff08;1&#xff09;master虚拟机免密登录master虚拟机&#xff08;2&#xff09;将公钥拷贝到所创建的虚拟机上 一、SSH免密登录功能配置 ssh密钥登录比密码登录安全&#xff0c;主要是因为他使用了非对称加密&#xff0c;登录过程中需…

CentOS开启SSH免密登录

CentOS开启SSH免密登录 要实现SSH免密登录&#xff0c;首先需要准备一组公钥和私钥。将公钥放到服务器上&#xff0c;将私钥放到客户机上。当客户机连接服务器时&#xff0c;服务器会根据自身的公钥校验客户机的私钥&#xff0c;如果校验通过则允许连接。 一、创建密钥 在客…

Ubuntu开启SSH免密登录

Ubuntu开启SSH免密登录 要实现SSH免密登录&#xff0c;首先需要准备一组公钥和私钥。将公钥放到服务器上&#xff0c;将私钥放到客户机上。当客户机连接服务器时&#xff0c;服务器会根据自身的公钥校验客户机的私钥&#xff0c;如果校验通过则允许连接。 一、创建密钥 在客…

git SSH免密登录

git系列文章目录 第八章 git SSH免密登录的使用 文章目录 git系列文章目录前言一、生成密钥二、使用步骤1.使用VSCODE打开.pub文件复制其中的内容2.打开github或者gitee进入设置选项设置密钥3.使用密钥 总结 前言 虽然Windows系统提供了凭据功能&#xff0c;但是介绍ssh提交&a…

配置SSH免密登录

配置SSH免密登录 1. 在各个虚拟机&#xff08;master、s1、s2&#xff09;家目录执行ssh-keygen -b 1024 -t rsa&#xff0c;输入2次回车&#xff0c;输入y/yes再继续回车 ssh-keygen -b 1024 -t rsa 2. 进入到.ssh目录中 ls -all #查看所有文件和文件夹 cd .ssh 查看目录 ls…

SSH免密登录原理

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 SSH免密登录原理 前言一、配置ssh二、无密钥配置1、免密登录原理2、生成公钥和私钥3、将公钥拷贝到要免密登录的目标机器上 三、.ssh文件夹下&#xff08;~/.ssh&#xff09;…

ssh免密登录

两台设备&#xff0c;第一台设备作为客户端&#xff0c;第二台设备作为服务端&#xff0c;在第一台使用一个用户免密登录第二台设备。 在第一台设备输入命令&#xff1a; ssh-keygen -t rsa -b 2048 输入命令执行后遇到选项可以选择默认&#xff0c;敲Enter继续执行便可 然后把…

VSCode SSH 免密登录

前提 VSCode 已经安装 Remote - SSH 插件&#xff0c;并且可以通过密码登录远程主机 步骤 假设 VSCode 运行在 Windows&#xff0c;SSH 远程登录 Linux 1、在 Windows 端生成公钥/私钥对 例如在 git bash 中运行 ssh-keygen&#xff0c;然后一路回车&#xff0c;直到出现下面…

SSH免密登录配置

免密登录命令&#xff1a; 1.进入.ssh目录&#xff1a; cd ~/.ssh 2.生成一对密钥&#xff1a; ssh-keygen -t rsa 3.发送公钥&#xff1a; ssh-copy-id 192.168.xx.xxx 4.免密登录测试&#xff1a; ssh 192.168.xx.xxx 目录 一、免密登录原理 二、配置ssh 1.查看 .…

SSH配置免密登录方法

转载自https://blog.csdn.net/jeikerxiao/article/details/84105529 1.客户端生成公私钥 本地客户端生成公私钥&#xff1a;&#xff08;一路回车默认即可&#xff09; ssh-keygen上面这个命令会在用户目录.ssh文件夹下创建公私钥 cd ~/.sshls下创建两个密钥&#xff1a; id…

Jwt登录退出

1.登录获取用户信息并写入token Overridepublic CommonResult loginV2(RcSysUserEntity byAccount) {log.info("********************");log.info("******************** account-V2:{}",byAccount.getAccount());log.info("********************&quo…

注册、登录、退出登录

运营商系统登录与安全控制 2.1需求分析 完成运营商登陆功能 (1)、登录页面 (2)登录后页面 (3)、点击右上角头像后显示。 2.2登陆功能的实现 2.2.1配置文件 (1)修改mall-manager-web的pom.xml ,添加依赖 <!-- 身份验证 --> <dependency> &l…

小程序登录与退出登录

主要是通过在storage中缓存userInfo与清空userInfo的信息来实现登录与退出登录 wxml&#xff1a; <view class"setting"><view class"setting_thr" bindtap"login">登录</view></view><view class"setting&qu…