基本数据结构:链表(list)

Redis 数据结构 Redis 3.0 的 List 对象在数据量⽐较少的情况下,会采⽤「压缩列表」作为底层数据结构的实现,它的优势是节省内存空间,并且是内存紧凑型的数据结构。然后在 Redis 5.0 设计了新的数据结构 listpack,沿⽤了压缩列表紧凑型的内存布局,最终在最新的 Redis 版本,将 Hash 对象和 Zset 对象的底层数据结构实现之⼀的压缩列表,替换成由 listpack 实现。Redis 只有在 Zset 对象的底层实现⽤到了跳表,跳表的优势是能⽀持平均 O(logN) 复杂度的节点查找。 阅读详情

谈到链表之前,先说一下线性表。线性表是最基本、最简单、也是最常用的一种数据结构。线性表中数据元素之间的关系是一对一的关系,即除了第一个和最后一个数据元素之外,其它数据元素都是首尾相接的。线性表有两种存储方式,一种是顺序存储结构,另一种是链式存储结构。

顺序存储结构就是两个相邻的元素在内存中也是相邻的。这种存储方式的优点是查询的时间复杂度为O(1),通过首地址和偏移量就可以直接访问到某元素,关于查找的适配算法很多,最快可以达到O(logn)。缺点是插入和删除的时间复杂度最坏能达到O(n),如果你在第一个位置插入一个元素,你需要把数组的每一个元素向后移动一位,如果你在第一个位置删除一个元素,你需要把数组的每一个元素向前移动一位。还有一个缺点,就是当你不确定元素的数量时,你开的数组必须保证能够放下元素最大数量,遗憾的是如果实际数量比最大数量少很多时,你开的数组没有用到的内存就只能浪费掉了。

我们常用的数组就是一种典型的顺序存储结构,如图1。


链式存储结构就是两个相邻的元素在内存中可能不是相邻的,每一个元素都有一个指针域,指针域一般是存储着到下一个元素的指针。这种存储方式的优点是插入和删除的时间复杂度为O(1),不会浪费太多内存,添加元素的时候才会申请内存,删除元素会释放内存,。缺点是访问的时间复杂度最坏为O(n),关于查找的算法很少,一般只能遍历,这样时间复杂度也是线性(O(n))的了,频繁的申请和释放内存也会消耗时间。

顺序表的特性是随机读取,也就是访问一个元素的时间复杂度是O(1),链式表的特性是插入和删除的时间复杂度为O(1)。要根据实际情况去选取适合自己的存储结构。

链表就是链式存储的线性表。根据指针域的不同,链表分为单向链表、双向链表、循环链表等等。

一、 单向链表(slist)

链表中最简单的一种是单向链表,每个元素包含两个域,值域和指针域,我们把这样的元素称之为节点。每个节点的指针域内有一个指针,指向下一个节点,而最后一个节点则指向一个空值。如图2就是一个单向链表。


一个单向链表的节点被分成两个部分。第一个部分保存或者显示关于节点的信息,第二个部分存储下一个节点的地址。单向链表只可向一个方向遍历。

我写了一个简单的C++版单向链表类模板,就用这段代码讲解一下一个具体的单向链表该怎么写(代码仅供学习),当然首先你要具备C++基础知识和简单的模板元编程。
完整代码

首先我们要写一个节点类,链表中的每一个节点就是一个节点类的对象。如图3。


代码如下:


template< class T>
class slistNode
{
     public:
    slistNode(){next=NULL;} // 初始化
    T data; // 值
    slistNode* next; // 指向下一个节点的指针
};

第二步,写单链表类的声明,包括属性和方法。

代码如下:


template< class T>
class myslist
{
     private:
    unsigned  int listlength;
    slistNode<T>* node; // 临时节点
    slistNode<T>* lastnode; // 头结点
    slistNode<T>* headnode; // 尾节点
     public:
        myslist(); // 初始化
        unsigned  int length(); // 链表元素的个数
         void add(T x); // 表尾添加元素
         void traversal(); // 遍历整个链表并打印
         bool isEmpty(); // 判断链表是否为空
        slistNode<T>* find(T x); // 查找第一个值为x的节点,返回节点的地址,找不到返回NULL
         void Delete(T x); // 删除第一个值为x的节点
         void insert(T x,slistNode<T>* p); // 在p节点后插入值为x的节点
         void insertHead(T x); // 在链表的头部插入节点
};

第三步,写构造函数,初始化链表类的属性。

代码如下:

template< class T>
myslist<T>::myslist()
{
    node=NULL;
    lastnode=NULL;
    headnode=NULL;
    listlength=0;
}

第四步,实现add()方法。

代码如下:

template< class T>
void  myslist<T>::add(T x)
{
    node= new slistNode<T>(); // 申请一个新的节点
    node->data=x; // 新节点赋值为x
     if(lastnode==NULL) // 如果没有尾节点则链表为空,node既为头结点,又是尾节点
    {
        headnode=node;
        lastnode=node;
    }
     else // 如果链表非空
    {
        lastnode->next=node; // node既为尾节点的下一个节点
        lastnode=node; // node变成了尾节点,把尾节点赋值为node
    }
    ++listlength; // 元素个数+1
}

第五步,实现traversal()函数,遍历并输出节点信息。

代码如下:

template< class T>
void  myslist<T>::traversal()
{
    node=headnode; // 用临时节点指向头结点
     while(node!=NULL) // 遍历链表并输出
    {
        cout<<node->data<<ends;
        node=node->next;
    }
    cout<<endl;
}

第六步,实现isEmpty()函数,判断链表是否为空,返回真为空,假则不空。

代码如下:

template< class T>
bool  myslist<T>::isEmpty()
{
     return listlength==0;
}

第七步,实现find()函数。

代码如下:

template< class T>
slistNode<T>* myslist<T>::find(T x)
{
    node=headnode; // 用临时节点指向头结点
     while(node!=NULL&&node->data!=x) // 遍历链表,遇到值相同的节点跳出
    {
        node=node->next;
    }
     return node; // 返回找到的节点的地址,如果没有找到则返回NULL
}

第八步,实现delete()函数,删除第一个值为x的节点,如图4。


代码如下:

template< class T>
void  myslist<T>::Delete(T x)
{
    slistNode<T>* temp=headnode; // 申请一个临时节点指向头节点
     if(temp==NULL)  return; // 如果头节点为空,则该链表无元素,直接返回
     if(temp->data==x) // 如果头节点的值为要删除的值,则删除投节点
    {
        headnode=temp->next; // 把头节点指向头节点的下一个节点
         if(temp->next==NULL) lastnode=NULL; // 如果链表中只有一个节点,删除之后就没有节点了,把尾节点置为空
        delete(temp); // 删除头节点
         return;
    }
     while(temp->next!=NULL&&temp->next->data!=x) // 遍历链表找到第一个值与x相等的节点,temp表示这个节点的上一个节点
    {
        temp=temp->next;
    }
     if(temp->next==NULL)  return; // 如果没有找到则返回
     if(temp->next==lastnode) // 如果找到的时候尾节点
    {
        lastnode=temp; // 把尾节点指向他的上一个节点
        delete(temp->next); // 删除尾节点
        temp->next=NULL;
    }
     else // 如果不是尾节点,如图4
    {
        node=temp->next; // 用临时节点node指向要删除的节点
        temp->next=node->next; // 要删除的节点的上一个节点指向要删除节点的下一个节点
        delete(node); // 删除节点
        node=NULL;
    }
}

第九步,实现insert()和insertHead()函数,在p节点后插入值为x的节点。如图5。



 

代码如下:

template< class T>
void  myslist<T>::insert(T x,slistNode<T>* p)
{
     if(p==NULL)  return;
    node= new slistNode<T>(); // 申请一个新的空间
    node->data=x; // 如图5
    node->next=p->next;
    p->next=node;
     if(node->next==NULL) // 如果node为尾节点
    lastnode=node;
}
template< class T>
void  myslist<T>::insertHead(T x)
{
    node= new slistNode<T>();
    node->data=x;
    node->next=headnode;
    headnode=node;
}

最终,我们完成一个简单的单向链表。此单向链表代码还有很多待完善的地方,以后会修改代码并不定时更新。

二、 双向链表

双向链表的指针域有两个指针,每个数据结点分别指向直接后继和直接前驱。单向链表只能从表头开始向后遍历,而双向链表不但可以从前向后遍历,也可以从后向前遍历。除了双向遍历的优点,双向链表的删除的时间复杂度会降为O(1),因为直接通过目的指针就可以找到前驱节点,单向链表得从表头开始遍历寻找前驱节点。缺点是每个节点多了一个指针的空间开销。如图6就是一个双向链表。



三、 循环链表

循环链表就是让链表的最后一个节点指向第一个节点,这样就形成了一个圆环,可以循环遍历。单向循环链表可以单向循环遍历,双向循环链表的头节点的指针也要指向最后一个节点,这样的可以双向循环遍历。如图7就是一个双向循环链表。

Calibre提参实战:如何用hcell和xcell文件精准屏蔽模块(附避坑指南) 本文详细解析了Calibre提参过程中hcell和xcell文件的应用技巧,帮助工程师精准屏蔽模块以优化寄生参数提取。通过对比hcell与xcell的核心差异、图形界面与命令行操作指南,以及常见问题排查方法,提升芯片后端设计效率。附避坑指南,解决EETOP论坛热议的模块丢失问题。 阅读详情

相关推荐

QCustomPlot实战:从入门到精通的数据可视化

本文详细介绍了如何使用QCustomPlot进行数据可视化,从基础环境搭建到高级图表定制,涵盖实时监控、多坐标系联动等实战技巧。基于Qt的QCustomPlot库以其强大的绘图能力和灵活的API,帮助开发者快速实现专业级数据可视化效果。

weixin_29003437的博客 115

C++数据结构——链表

链表是一种动态数据结构,它通过指针将一组零散的内存块串联起来使用。它作为基础数据结构,其实现质量直接影响程序性能和稳定性。

Daniel0429的博客 4379

ESP32搭配LVGL显示图片的两种方法详解:内部存储 vs 外部文件

本文详细介绍了ESP32搭配LVGL显示图片的两种方法:内部存储与外部文件。内部存储方案适合快速响应的小图片,而外部文件方案则适用于需要灵活更新和大容量图片的场景。通过对比分析,帮助开发者根据项目需求选择最优方案,提升嵌入式系统的UI性能。

weixin_30399821的博客 518

手把手教你写链表,小学生看了都说好~

摘要:明明我们在之前已经接触了数组,感到数组已经是万能的数据存储位置了。但是,如果我们一直在使用比较复杂的数据(也就是比较多的数据时),肯定会感到很反感。因为对于数组这种数据结构,在你自己使用之前,一定要对其大小进行一番定义。这样一来,它的存储空间在数据处理过程中便显得极为不方便。因为谁也不想对将要处理的数据做一个空间的预算,这几乎是所有程序员都很忌讳的,并且还要让其空间足够大,这样才能满足我们的要求(但如果分配的太多,难免会浪费内存)。 所以,这就是为啥你要用链表、学链表。链表是一种数据结构,它弥补了数组

weixin_39266374的博客 910

中国30米分辨率城市建成区数据集 (1972-2023年)

全国城市建成区空间分布数据(1972-2023年),利用Google Earth Engine平台,基于Landsat系列遥感影像提取了1972–2023年城市不透水面,后经过不透水面聚集密度计算方法计算了不透水面聚集密度,通过阈值划分提取了城市建成区,并设置了建成区面积阈值,去除了噪点,获取了全国城市建成区空间分布数据集。该提取能准确刻画城市形态,提取精度高,保持了建成区的完整性。数据集包括41期数据,1972年,1978年,1985-2023年逐年,分辨率为30米,有栅格和矢量两种格式。

地球资源数据云的官方博客 5824

借助AI高效写出结构严谨、内容专业的课题申报书:10大核心模块拆解(附实用提示词)

在很多老师和科研人员眼中,写一份高质量的课题申报,确实是一件非常困难的事。难就难在不只是要说清楚写了什么研究内容,还要体现申报书的可行性和创新性。七哥除了自己写课题,也经常指导会员同仁使用高级学术版搞定课题的申报工作,摸索出了一套行之有效的借助AI申报课题的技巧,在这篇文章中,我将给大家点明课题申报书的10大核心模块的写作技巧,还会给大家一些提示词模板供大家参考,如果你也正准备申报课题,那么这篇文章一定不要错过!特别说明的是,提示词的使用并不是一蹴而就的,是专业思考的延伸和AI化专业表达,过程中需要多次的调

智能AI让学术科研更容易 441

基本数据结构:链表(list) - C小加 - C++博客

基本数据结构:链表(list) - C小加 - C++博客 基本数据结构:链表(list) - C小加 - C++博客基本数据结构:链表(list)作者:C小加更新时间:2012-7-31谈到链表之前,先说一下线性表。线性表是最基本、最简单、也是最常用的一种数据结...

526

PCL 计算点云法向量并显示【2026最新版】

PCL计算法向量并显示,用OMP进行计算加速以及法线定向的深层次理解、操作。博客长期更新,本文最近更新时间为:2026年1月28日。

点云侠的博客 32万+

基本的数据结构:单链表(Singly Linked List)

一.基本的数据结构单链表(Singly Linked List) 什么是单链表? 下面是百度百科给出的官方解释: 单链表是一种链式存取的数据结构,用一组地址任意的存储单元存放线性表中的数据元素。链表中的数据是以结点来表示的,每个结点的构成:元素(数据元素的映象) + 指针(指示后继元素存储位置),元素就是存储数据的存储单元,指针就是连接每个结点的地址数据。 其实通俗的来讲,单链表就是结构体变量...

weixin_44795839的博客 3626

PS射击游戏射线检测优化:原理、工程与全链路高性能实现

射线池:减少GC,提升批量检测效率。分层检测:先粗后细,远距离射击高效判定。批量检测:并行处理,适合大量射线场景。空间结构:进一步加速大场景检测。下面继续深入射线检测优化,补充更多实用细节和进阶技巧,包括空间分区加速、射线检测多线程/异步化、射线合并与分帧、射线池通用模板、常见性能陷阱与调优建议等。对于极端性能需求,可自定义轻量级物理系统,仅检测关键对象(如玩家、子弹),跳过复杂物理引擎。结合AABB、OBB、球体等简单碰撞体,射线-包围体检测极快。空间分区+分层检测是大场景射线优化核心。

本博客聚焦游戏开发技巧、创业实战心得及大学热门课程干货。这里既有技术深度,也有思维广度,无论你是开发者、创业者还是高校学子,都能在这里找到适合自己的成长之路! 434

单线激光雷达的工作原理

单线激光雷达通过单一激光束的水平扫描和时间 / 相位测量,实现二维环境的距离感知,是低成本二维导航的核心传感器。尽管其无法直接提供三维信息,但其可靠性和经济性使其在消费级和工业级场景中广泛应用。随着技术发展,部分单线雷达通过振动镜或 MEMS 微振镜实现微小角度偏转,可近似模拟多线扫描效果,进一步拓展了应用边界。

谢谢大家的关注和点赞!这里只有纯纯的知识干货,没有一句废话。希望能实实在在帮到大家~要是觉得有用,别忘了给我点支持哟,你 1859

基本数据结构:链表(list)(1)

基本数据结构:链表(list) 作者:C小加 更新时间:2012-7-31 谈到链表之前,先说一下线性表。线性表是最基本、最简单、也是最常用的一种数据结构。线性表中数据元素之间的关系是一对一的关系,即除了第一个和最后一个数据元素之外,其它数据元素都是首尾相接的。线性表有两种存储方式,一种是顺序存储结构,另一种是链式存储结构。 顺序存储结构就是两个相邻的元素在内存中也...

weixin_34245749的博客 132

AUTOSAR MCAL开发及EB Tresos工具使用总结

在基于AutoSar CP架构开发MCAL时,会使用到EB tresos工具。本文主要对MCAL开发及EB tresos工具使用进行总结,仅作为自学使用,以方便后续查漏补缺。

liuxu324的博客 2625

基本数据结构:链表(list)(2)

接上篇 第九步,实现insert()和insertHead()函数,在p节点后插入值为x的节点。如图5。 代码如下: template<classT>voidmyslist<T>::insert(Tx,slistNode<T>*p){if(p==NULL)return;...

weixin_33762130的博客 77

假人有限元模型

假人模型,车辆碰撞假人有限元模型,适合做乘员安全的研究者利用

visio安装包

Microsoft Office Visio 是一款专业的制图软件。利用经过更新的形状、协作工具和数据链接图表,创建专业级图表以简化复杂的信息。

Linux内核数据结构之链表

链表一、链表结构1、单向链表2、双向链表3、环形链表二、Linux内核中链表的实现 和其他很多大型项目一样,Linux内核实现了很多通用的数据结构,而且提倡大家在开发时重用。 一、链表结构 链表是一种存放和操作可变数量元素(常称为节点)的数据结构。链表和静态数组的不同之处在于,链表所包含的元素都是动态创建并插入链表的,在编译的时候不必知道具体需要创建多少元素,所以它们在内存中无须占用连续内存区。正是因为元素不连续地存放,所以各元素需要通过某种方式被连接在一起。 1、单向链表 每个元素拥有一个指向下一个元素的

年少生而为人 726

efm32 入门.pdf

本PDF共计22页,介绍的是操作驱动API,下载与否自己斟酌。

C++链表解析:从基础原理到高级应用,全面掌握链表的使用

C++链表解析:从基础原理到高级应用,全面掌握链表的使用

探索C++编程的奥秘,分享深入的技术见解和实践,旨在激发读者创造力与解决问题的思维。 1880

android 蓝牙通讯demo

android 下蓝牙通讯demo,实现了发现蓝牙,使能蓝牙,通过蓝牙发送消息。

Redis学习笔记--Redis基本类型及其数据结构

什么是Redis? Redis(全称:Remote Dictionary Server 远程字典服务)是一个开源的使用ANSIC语言编写、支持网络、可基于内存亦可持久化的日志型、Key-Value数据库,并提供多种语言的API。它通常被称为数据结构服务器,因为值(value)可以是 字符串(String), 哈希(Hash), 列表(list), 集合(sets) 和 有序集合(sorted ...

安天然的博客 1017
上一篇: 大话设计模式——序
下一篇: == equals new String("abc")
JJ_wang
博客等级 码龄14年 2粉丝 10原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值