一文详解动态链表和静态链表的区别

news2024/11/17 13:42:33

1、引言

        本文主要是对动态链表和静态链表的区别进行原理上的讲解分析,先通过对顺序表和动态链表概念和特点的原理性介绍,进而引申出静态链表的作用,以及其概念。通过这些原理性的概述,最后总结归纳出动态链表和静态链表的区别。本文不对代码进行额外的讲解,只对原理进行分析以加深基础的认识,相关概念的代码应用读者可以另行在网上进行搜索详细学习。


2、顺序表和动态链表的特点

        首先需要明白的是,顺序表和链表都是线性表,即线性存储结构。使用线性表存储数据的方式可以这样理解,即“把所有数据用一根线串起来,再存储到物理空间中”。

        如下图左边将数据依次存储在连续的整块物理空间中,这种存储结构称为顺序存储结构(简称顺序表);下图右边数据分散的存储在物理空间中,通过一根线保存着它们之间的逻辑关系,这种存储结构称为链式存储结构(简称链表)。可以看出每一个数据按照“一对一”的关系按照次序逐个排列。

图片

2.1 顺序表存储结构

        顺序表对数据的物理存储结构有要求,需预先申请一整块足够大的存储空间,然后将数据依次存储起来,数据之间紧密贴合,不留一丝空隙。如下图所示,顺序表数据的 '一对一' 逻辑关系就是数据按照次序连续存储到一整块物理空间上,一个数据存储在一个位置之后紧接着就是下一个数据存储在下一个连续的位置。其数据存储方式和数组非常相似。

图片

        使用顺序表存储数据之前,除了要申请足够大小的物理空间之外,为了方便后期使用表中的数据,顺序表还需要实时记录以下 2 项数据:(1)顺序表申请的存储容量,即总的空间大小,类似于数组的总大小;(2)顺序表的长度,也就是当前表中存储数据元素的个数。正常状态下,顺序表申请的存储容量要大于顺序表的长度。顺序表的结构表示如下:

typedef struct Table
{
    int * head;//声明了一个名为head的长度不确定的数组,也叫“动态数组”
    int length;//记录当前顺序表的长度
    int size;//记录顺序表分配的存储容量
}table;

2.2 链表存储结构

        链表的存储方式与顺序表截然相反,链表不限制数据的物理存储状态,换句话说,使用链表存储的数据元素,其物理存储位置不是连续的,而是随机的。什么时候存储数据,才在什么时候申请存储空间。链表数据的 '一对一' 逻辑关系就是数之间通过指针来维持,一个数据存储在一个位置之后通过指针指向下一个数据存储在下一个位置。这样的链表,也称之为"动态链表"。

图片

        链表中每个数据的存储都由两部分组成:(1)数据元素本身,其所在的区域称为数据域;(2)指向直接后继元素的指针,所在的区域称为指针域。这两个部分组成了链表中的一个数据节点,链表实际存储的是一个一个的节点,链表数据节点的结构表示如下:

typedef struct Link
{
    char elem; //代表数据域
    struct Link * next; //代表指针域,指向直接后继元素
}link; //link为节点名,每个节点都是一个 link 结构体

2.3 顺序表和动态链表的比较

        根据以上介绍的顺序表和动态链表的存储结构特点,可以比较出两者在以下几个方面的区别:

(1)开辟空间的方式

        顺序表存储数据实行的是 "一次开辟,永久使用",即存储数据之前先开辟好足够的存储空间,空间一旦开辟后期无法改变大小(使用动态数组malloc的情况除外)。

而动态链表则不同,动态链表存储数据时一次只开辟存储一个节点的物理空间,如果后期需要还可以再申请。

        因此,若只从开辟空间方式的角度去考虑,当存储数据的个数无法提前确定,又或是物理空间使用紧张以致无法一次性申请到足够大小的空间时,使用动态链表更有助于问题的解决。

(2)空间利用率

        顺序表的空间利用率高,而动态链表的空间利用率低。

        顺序表用一段连续的存储单元依次存储线性表的数据元素,物理空间上是连续的;动态链表用一组任意的存储单元存放线性表的元素,逻辑上连续(通过指针维持),但物理空间上不一定连续。

        动态链表在物理空间上不一定连续是由于每次只申请一个节点的空间,且空间的位置是随机的。这种申请存储空间的方式会产生很多空间碎片,一定程度上造成了空间浪费。不仅如此,由于动态链表中每个数据元素都必须携带至少一个指针,因此,动态链表对所申请空间的利用率没有顺序表高。

(3)插入元素的容量

        顺序表中,空间不够时需要扩容,扩容会有一定的消耗,扩容后可能存在一定的空间浪费;动态链表没有容量的概念,按需申请空间。

(4)存储密度

        顺序表中,其存储密度均为1,因为数组空间只用来存数据元素。而在动态链表中,每个节点除了存储数据元素自身外,还会至少存储直接后继的存储位置信息。相对于顺序表,动态链表的存储密度要低得多。

(5)时间复杂度

针对随机访问性能来说:

        顺序表随机访问一个元素可以用下标的方式直接访问,无需遍历整个表,时间复杂度为O(1);动态链表随机访问一个元素,需要从头到尾遍历,直到寻找到该元素,时间复杂度为O(N)。

针对任意位置插入或者删除元素来说:

        顺序表可能需要按顺序搬移大量元素后进行元素的插入或删除,效率较低,时间复杂度为O(N);动态链表中数据元素之间的逻辑关系靠的是节点之间的指针,因此在动态链表中插入或删除元素只需修改指针的指向,不需搬移大量元素,时间复杂度为O(1)。

        因此涉及访问元素的操作,而元素的插入、删除和移动操作极少的场景时,适合用顺序表;涉及元素的插入、删除和移动,而访问元素的需求很少的场景时,适合用动态链表。


3、为什么会有静态链表

        单站在时间复杂度的角度上来看,是否能够有一种数据结构既能够像顺序表一样快速的访问数据元素,又可以像动态链表一样可以方便的插入、删除和移动数据元素?

        静态链表就是这样一种数据结构,其属于一种线性存储结构,分配一整片连续的内存空间,各个节点集中安置,逻辑结构上相邻的数据元素,存储在指定的一块内存空间中,数据元素只允许在这块内存空间中随机存放。数据全部存储在数组中(和顺序表一样),但存储位置是随机的,数据之间"一对一"的逻辑关系通过一个整型变量(称为"游标",和指针功能类似)维持(和链表类似)。在将数据存放到数组中时,给各个数据元素配备一个整形变量,此变量用于指明各个元素的直接后继元素所在数组中的位置下标。

        如图中a[0]~a[6]为数组下标,分配的内存空间中绿色数字为数组的数据元素,红色数字就为数组的游标变量,表示当前节点的下一个节点的数组下标。因此下标为x的数组中存放当前的数组数据元素和后继元素所在数组中的位置下标。因此可以看出静态链表是用数组来实现链式存储结构,静态链表实际上就是一个结构体数组。

图片

        如上图:a[1]中存放的数据元素值为2,通过a[1]中存放的游标变量4可找到后继元素所在的数组a[4];a[4]中存放的数据元素值为5,通过a[4]中存放的游标变量3可找到后继元素所在的数组a[3];a[3]中存放的数据元素值为7,通过a[3]中存放的游标变量6可找到后继元素所在的数组a[6]。以此类推,直到某元素的游标变量为0即可停止(注意:a[0]为头结点,其不存储数据元素)。

        但是从上图中,可以看出数组中间有未使用过的空间即没有数据元素的数组成员,这样岂不是浪费了存储空间?为了使我们创建的空间能够得到充分的利用,我们还需要一条连接各个空闲位置的链表,方便我们的随取随用,这条链表也被称为备用链表。

        备用链表的作用是回收数组中未使用或之前使用过(目前未使用)的存储空间,留待后期使用。也就是说,静态链表使用数组申请的物理空间中,存有两个链表,实现不同的功能,一个用来连接数据,另一个用来连接数组中的空闲空间。

图片

        为了适应这个,会存在一个“潜规则”,默认备用链表的表头位于数组下标为0(a[0])的位置,而数据链表的表头位于数据下标为1(a[1])的位置。备用链表的数组成员中仅存放游标。如上图所示:备用链表的连接顺序依次是:a[0]、a[2]、a[5],数据链表的连接顺序依次是a[1]、a[3]、a[4]、a[6]。

        静态链表中设置备用链表的好处是:可以清楚地知道数组中是否有空闲位置,以便数据链表添加新数据时使用。在静态链表的插入和删除操作中,都会与备用链表有着联系,当进行插入时,则是用备用链表上面取得一个节点作为待插入的新节点,反之,当在删除时,则将从链表上删除下来的节点链接到备用链表上面。整个过程中,我们需要做的工作就是更新游标的值。

        可见静态链表由数据链表的数据链表的各个节点和的备用链表的各个节点组成,静态链表节点的结构表示如下:

typedef struct List
{
    int data;//数据域
    int cur;//游标
}list;

4、动态链表和静态链表的区别

        通过以上对动态链表和静态链表原理概念和各自特点的介绍,我们可以对两者的区别有一个更深的认知。

(1)链表中数据“一对一”的关系

        动态链表是靠指针来维持。

        静态链表是靠游标来维持。

(2)内存空间申请

        使用动态链表存储数据,不需要预先申请内存空间,而是在需要的时候才向内存申请。也就是说,动态链表存储数据元素的个数是不限的,想存多少就存多少。

        使用静态链表存储数据,需要预先申请足够大的一整块内存空间,也就是说,静态链表存储数据元素的个数从其创建的那一刻就已经确定,后期无法更改。

(3)物理地址

        动态链表malloc 或 free 函数动态申请或释放内存,在长度上没有限制。因为是动态申请内存的,所以每个节点的物理地址不连续

        静态链表类是似于数组方法实现的,在物理地址上是连续的,而且需要预先分配地址空间大小,所以静态链表的初始长度一般是固定的。

(4)操作方式

        使用动态链表只需操控一条存储数据的链表。当表中添加或删除数据元素时,只需要通过 malloc 或 free 函数来申请或释放空间。

        静态链表是在固定大小的存储空间内随机存储各个数据元素,使用静态链表存储数据,需要操控两条链表,一条是存储数据的数据链表,另一条是记录空闲空间位置的备用链表,便于随时分配给新添加元素使用。当表中添加或删除数据元素时,需要更新数据链表和备用链表的对应节点的值。

(5)元素的访问

        动态链表随机访问一个元素,需要从头到尾遍历,直到寻找到该元素,时间复杂度为O(N)。

        静态链表随机访问一个元素,可以类似像数组通过下标的方式,通过游标来访问,时间复杂度为O(1)。

(6)元素的插入和删除

        动态链表插入或删除元素时不用做元素的移动,修改指针域即可。

        静态链表插入或删除元素时不用做元素的移动,可以通过修改游标的值来达到。


↓↓↓更多技术内容和书籍资料获取,入群技术交流敬请关注“明解嵌入式”↓↓↓ 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1063274.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

vector的介绍以及使用方式

目录 前言 1.vector的介绍 2.构造函数 3.迭代器 4.vector空间增长问题 5.vector的增删改查 6.vector迭代器失效问题 总结 前言 即我们的string之后,今天小编给大家要介绍一个我们stl中另外一个常用的容器vector,和我们的string一样我们的vector…

Vue中如何进行分布式任务调度与定时任务管理

在Vue中进行分布式任务调度与定时任务管理 分布式任务调度和定时任务管理是许多应用程序中的关键功能之一。它们用于执行周期性的、异步的、重复的任务,例如数据备份、邮件发送、定时报告生成等。在Vue.js应用中,我们可以结合后端服务实现分布式任务调度…

浏览器技巧:谷歌浏览器六个实用设置小技巧,值得收藏

目录 1、确保你的浏览器启用标准保护选项 2、使用安全DNS(DNS over HTTPS) 3、网站通知修改为"静态指示方式" 4、启用页面预加载提升网页加载速度 5、阻止Chrome浏览器在后台运行 6. 更改 Chrome 启动后打开方式为"上次打开的网页&…

javaWeb超市订单管理系统

一、引言 超市管理系统(smbms)作为每个计算机专业的大学生都是一个很好的练手项目,逻辑层次分明,基础功能包括用户的登录和注销,用户和供应商以及订单信息的增删查改的基础功能。可以帮助我们更好的加深理解三层架构的理念,本项目…

复习 --- QT服务器客户端

服务器&#xff1a; 头文件&#xff1a; #ifndef WIDGET_H #define WIDGET_H#include <QWidget> #include<QTcpServer> #include<QTcpSocket> #include<QMessageBox> #include<QDebug> #include<QList> #include<QListWidget> #in…

电脑数据恢复怎么操作?电脑数据恢复难点是什么

随着电脑在我们日常生活中的普及&#xff0c;数据的重要性不言而喻。然而&#xff0c;在某些情况下&#xff0c;我们可能会不小心删除或因其他原因导致丢失了重要的电脑数据&#xff0c;这时候就需要进行数据恢复操作。下面我们一起来了解下电脑数据恢复的操作方法&#xff0c;…

【全3D打印坦克——基于Arduino履带式机器人】

【全3D打印坦克——基于Arduino履带式机器人】 1. 概述2. 设计机器人平台3. 3D 模型和 STL 下载文件3.1 3D打印3.2 组装 3D 打印坦克 – 履带式机器人平台3.3 零件清单 4. 机器人平台电路图4.1 定制电路板设计4.2 完成 3D 打印储罐组件 5. 机器人平台编程6. 测试3D打印机器人 -…

侯捷 C++ STL标准库和泛型编程【C++学习笔记】 超详细 万字笔记总结 笔记合集

关于STL这部分&#xff0c;原课程将其分为了四部分&#xff0c;我做笔记时&#xff0c;会将其整合&#xff0c;使其更具有整体性 文章目录 1 STL概述1.1 头文件名称1.2 STL基础介绍1.3 typename 2 OOP vs. GP3 容器3.1 容器结构分类3.2 序列式容器3.2.1 array测试深度探索 3.2.…

Python3操作MongoDb7最新版创建文档及CRUD基本操作

Python3中类的高级语法及实战 Python3(基础|高级)语法实战(|多线程|多进程|线程池|进程池技术)|多线程安全问题解决方案 Python3数据科学包系列(一):数据分析实战 Python3数据科学包系列(二):数据分析实战 Python3数据科学包系列(三):数据分析实战 MongoDB 操作手册----文档…

Zookeeper经典应用场景实战(一)

文章目录 1、Zookeeper Java客户端实战1.1、 Zookeeper 原生Java客户端使用1.2、 Curator开源客户端使用 2、 Zookeeper在分布式命名服务中的实战2.1、 分布式API目录2.2、 分布式节点的命名2.3、 分布式的ID生成器 3、Zookeeper实现分布式队列3.1、 设计思路3.2、 使用Apache …

电脑桌面黑屏,但程序还可以正常运行

问题&#xff1a;桌面黑屏&#xff0c;程序可以正常运行操作 解决方法: 1.Ctrl Alt Del 2.点击 【任务管理器】-->【文件F】-->【运行新任务N】 3.输入 explorer.exe 回车

Docker 镜像的缓存特性

Author&#xff1a;rab 目录 前言一、构建缓存二、Pull 缓存总结 前言 首先我们要清楚&#xff0c;Docker 的镜像结构是分层的&#xff0c;镜像本身是只读的&#xff08;不管任何一层&#xff09;&#xff0c;当我们基于某镜像运行一个容器时&#xff0c;会有一个新的可写层被…

Spring的AOP开发-注解方式开发AOP

基于注解配置的AOP 注解方式AOP的基本使用 Spring的AOP也提供了注解方式配置&#xff0c;使用相应的注解替代之前的xml配置&#xff0c;xml配置AOP时&#xff0c;我们主要配置了三部分&#xff1a;目标类被Spring容器管理&#xff08;注解使用Service&#xff09;、通知类被S…

图像和视频上传平台Share Me

本文完成于 6 月&#xff0c;所以反代中&#xff0c;域名演示还是使用的 laosu.ml&#xff0c;不过版本并没有什么变化&#xff1b; 什么是 Share Me &#xff1f; Share Me 是使用 Next.js 和 PocketBase 的自托管图像和视频上传平台&#xff0c;具有丰富的嵌入支持和 API&…

基于Java的高校宿舍管理系统设计与实现(源码+lw+部署文档+讲解等)

文章目录 前言具体实现截图论文参考详细视频演示为什么选择我自己的网站自己的小程序&#xff08;小蔡coding&#xff09;有保障的售后福利 代码参考源码获取 前言 &#x1f497;博主介绍&#xff1a;✌全网粉丝10W,CSDN特邀作者、博客专家、CSDN新星计划导师、全栈领域优质创作…

【C++】unordered_map和unordered_set

哈希表 1. unordered_map1.1 概念1.2 常见接口 2. unordered_set2.1 概念2.1 常见接口 3. 底层实现3.1 哈希3.2 哈希函数3.3 闭散列和开散列3.3.1 闭散列3.3.2 开散列 3.4 模拟实现3.4.1 改造哈希桶3.4.2 模拟实现unordered_set3.4.3 模拟实现unordered_map 在C11中&#xff0c…

Promise, async, await 学习

异步编程简介&#xff1a; 介绍&#xff1a;异步编程是一种编程范式&#xff0c;旨在提高程序的性能和响应能力。该模型允许程序在执行某些任务时&#xff0c;不必等待这些任务完成就可以进行下一步操作&#xff0c;从而提高了程序的效率。 作用&#xff1a;异步编程通常用于…

IPT2602协议-USB 快速充电端口控制器

产品描述&#xff1a; IPT2602是一款USB端口快速充电协议控制芯片。IPT2602智能识别多种快速充电协议&#xff0c;对手机等受电设备进行快速充电。IPT2602根据受电设备发送的电压请求能够精确的调整VBUS输出电压&#xff0c;从而实现快速充电。 IPT2602在调整5V输出电压前会自动…

10.5 认识XEDParse汇编引擎

XEDParse 是一款开源的x86指令编码库&#xff0c;该库用于将MASM语法的汇编指令级转换为对等的机器码&#xff0c;并以XED格式输出&#xff0c;目前该库支持x86、x64平台下的汇编编码&#xff0c;XEDParse的特点是高效、准确、易于使用&#xff0c;它可以良好地处理各种类型的指…

[硬件基础]-快速了解RS232串行通信

快速了解RS232串行通信 文章目录 快速了解RS232串行通信1、概述2、什么是串行数据通信&#xff1f;3、什么是RS232&#xff1f;4、RS232应用5、RS232如何工作&#xff1f;6、RS232协议基础6.1 电压与逻辑表示6.2 数据编码6.3 起始位和停止位6.4 奇偶校验位6.5 波特率6.5 RS232电…