目录

一. 线性表

二. 顺序表的概念与结构

2.1 核心概念

2.2 两种常见结构

静态顺序表

动态顺序表

2.3 核心区别对比

四. 顺序表的实现

4.1 顺序表的定义

4.2 顺序表初始化

4.3 动态顺序表容量检查与扩容

4.4 动态顺序表插入数据

4.4.1 头插

4.4.2 尾插

4.4.3 指定位置插入

4.5 动态顺序表的删除数据

4.5.1 头删

4.5.2 尾删

4.5.3 删除指定位置的数据

4.5.4 删除指定的数据

4.6 查找指定数据

4.6 动态顺序表销毁

五. 优缺点分析

六. 适用场景选择

七. 总结


在学习顺序表之前 我们需要有一定的基础知识 首先我们需要了解线性表

一. 线性表

线性表是具有 “一对一” 逻辑关系的有限数据元素序列,是数据结构中最基础、最常用的结构之一,广泛应用于数组、链表等底层实现,也是栈、队列等复杂结构的基础。

核心定义与逻辑特征

线性表由 n(n≥0) 个相同数据类型的元素构成,记为 L = (a₁, a₂, ..., aₙ),其中:

  • a₁ 是首元素(无前驱),aₙ 是尾元素(无后继);

  • 除 a₁ 和 aₙ 外,每个元素 aᵢ(2≤i≤n-1) 有且仅有一个前驱aᵢ₋₁)和一个后继aᵢ₊₁);

  • n=0 时称为空表(无任何元素)。

两种核心存储结构( 暂时只做了解 )

线性表的存储结构决定了其操作效率,核心分为 “顺序存储” 和 “链式存储” 两类,二者各有优劣:

对比维度顺序存储结构(顺序表)链式存储结构(链表)
存储方式连续的存储单元依次存储元素任意存储单元存储元素,通过指针 / 引用链接逻辑关系
逻辑与物理关系逻辑顺序 ≡ 物理顺序逻辑顺序 ≠ 物理顺序(靠指针维护)
访问效率支持随机访问(通过索引 O(1) 定位)仅支持顺序访问(需从表头遍历,O(n)
插入 / 删除效率中间 / 头部操作需移动元素(O(n)),尾部插入 O(1)仅需修改指针(O(1),前提是找到目标位置)
空间利用率可能存在 “内存碎片”(预分配空间未用完)无碎片,但需额外存储指针(空间开销略大)
典型实现数组(如 C 语言数组、Java ArrayList)单链表、双向链表、循环链表(如 Java LinkedList)

二. 顺序表的概念与结构

顺序表是线性表的顺序存储结构,核心是用一段地址连续的物理存储单元依次存储线性表中的元素,使得元素的 “逻辑顺序” 与 “物理存储顺序完全一致”(即第 1 个元素存在第 1 个物理位置,第 2 个元素存在第 2 个物理位置,以此类推)。

2.1 核心概念

顺序表本质是 “基于数组的线性表实现”,但相比普通数组,它会额外记录当前元素个数(避免越界)和数组容量(方便扩容),是对数组的 “结构化封装”,确保符合线性表的逻辑特征(一对一前驱 / 后继关系)。

例如:存储 [1,3,5,7] 的顺序表,物理存储上元素在内存中连续排列,逻辑上 1 的后继是 33 的后继是 5,与物理位置顺序完全匹配。

2.2 两种常见结构

根据数组空间的分配方式,顺序表分为 “静态顺序表” 和 “动态顺序表”,实际开发中动态顺序表更常用(避免空间浪费或不足)。

静态顺序表

  • 特点:使用固定大小的数组存储元素,容量在定义时确定,无法动态调整。
  • 结构定义
    #define MAX_SIZE 100  // 固定容量
    typedef int SLDataType;  // 元素类型(可替换为其他类型)// 静态顺序表结构
    typedef struct SeqList {SLDataType arr[MAX_SIZE];  // 固定大小的数组,存储元素int size;                  // 当前元素个数(关键:记录有效元素数量)
    } SeqList;
    
  • 缺点:容量固定,若元素个数超过 MAX_SIZE 会溢出;若元素少则浪费内存。

动态顺序表

  • 特点:使用动态内存分配的数组(如 malloc/realloc 分配),容量可根据元素个数动态扩容 / 缩容,灵活性更高。
  • 结构定义
    typedef int SLDataType;// 动态顺序表结构
    typedef struct SeqList {SLDataType* arr;  // 指向动态分配数组的指针(存储元素)int size;         // 当前元素个数(有效元素数)int capacity;     // 当前数组的容量(最大可存储元素数)
    } SeqList;
    
  • 核心优势:容量按需调整,避免静态顺序表的空间浪费或溢出问题,是实际开发中的主流选择。

2.3 核心区别对比

对比维度静态顺序表动态顺序表
存储空间分配编译时固定分配(栈上或全局区)运行时动态分配(堆内存)
容量特性容量固定,不可改变容量可变,可动态扩容 / 缩容
空间利用率可能浪费(容量大于实际需求)利用率高(按需分配)
溢出风险有(元素数超过最大容量时)无(可动态扩容避免溢出)
内存管理复杂度简单(自动分配和释放)复杂(需手动分配、扩容和释放)
实现难度简单稍复杂(需实现扩容逻辑)

四. 顺序表的实现

4.1 顺序表的定义

静态顺序表:

#define MAX_SIZE 100  // 固定容量
typedef int SLDataType;  // 元素类型(可替换为其他类型)// 静态顺序表结构
typedef struct SeqList {SLDataType arr[MAX_SIZE];  // 固定大小的数组,存储元素int size;                  // 当前元素个数(关键:记录有效元素数量)
} SeqList;

动态顺序表:

typedef int SLDataType;// 动态顺序表结构
typedef struct SeqList {SLDataType* arr;  // 指向动态分配数组的指针(存储元素)int size;         // 当前元素个数(有效元素数)int capacity;     // 当前数组的容量(最大可存储元素数)
} SeqList;

4.2 顺序表初始化

静态顺序表初始化

void StaticSeqListInit(StaticSeqList* ssl) {ssl->size = 0;  // 只需初始化元素个数为0,数组空间已固定分配
}

动态顺序表初始化

void DynamicSeqListInit(DynamicSeqList* dsl) {dsl->data = NULL;dsl->size = 0;dsl->capacity = 0;  // 初始容量为0,或可分配默认初始容量

4.3 动态顺序表容量检查与扩容

// 检查容量,不足则扩容
void CheckCapacity(DynamicSeqList* dsl) {if (dsl->size == dsl->capacity) {// 计算新容量,初始容量为0则设为4,否则翻倍int newCapacity = dsl->capacity == 0 ? 4 : dsl->capacity * 2;// 重新分配内存SLDataType* newData = (SLDataType*)realloc(dsl->data, newCapacity * sizeof(SLDataType));if (newData == NULL) {perror("realloc failed");exit(EXIT_FAILURE);}dsl->data = newData;dsl->capacity = newCapacity;}
}

4.4 动态顺序表插入数据

4.4.1 头插

//头插
void SLPushFront(SL* ps, SLdatatype x)
{assert(ps);//ps!=NULL//检查空间是否足够SLCheckCapacity(ps);//空间足够for (int i = ps->size; i > 0; i--){ps->arr[i] = ps->arr[i - 1];}ps->arr[0] = x;ps->size++;
}

4.4.2 尾插

//尾插
void SLPushBack(SL* ps, SLTDataType x)
{//空间不够if (ps->size == ps->capacity){//增容int newCapacity = ps->capacity ==0 ? 4 : 2 * ps->capacity;SLTDataType* tmp = (SLTDataType*)realloc(ps->arr, newCapacity*sizeof(SLTDataType));if (tmp = NULL){perror("realloc");exit(1);}ps->arr = tmp;ps->capacity = newCapacity;}//空间足够ps->arr[ps->size++] = x;//插入完后,size往后走
}

4.4.3 指定位置插入

// 在pos位置插入元素value
int DynamicSeqListInsert(DynamicSeqList* dsl, int pos, SLDataType value) {if (pos < 0 || pos > dsl->size) return -1;CheckCapacity(dsl);  // 自动检查并扩容// 移动元素for (int i = dsl->size; i > pos; i--) {dsl->data[i] = dsl->data[i-1];}// 插入新元素dsl->data[pos] = value;dsl->size++;return 0;
}

4.5 动态顺序表的删除数据

4.5.1 头删

//尾删
void SLPopBack(SL* ps)
{assert(ps && ps->size);ps->size--;
}

4.5.2 尾删

//尾删
void SLPopBack(SL* ps)
{assert(ps && ps->size);ps->size--;
}

4.5.3 删除指定位置的数据

void  SQdelete(SL* ps, int pos) //删除第x个数据 这个x是数组的下标
{assert(ps->size > 0 && ps->size > pos);while (ps->size-pos>0){ps->arr[pos] = ps->arr[pos + 1];pos++;}ps->size--;
}

4.5.4 删除指定的数据

void SQLdex(SL* ps, int x) //删除查找到的所有x
{int i = 0;while (i < ps->size){if (ps->arr[i] == x){// 元素前移覆盖要删除的元素for (int j = i; j < ps->size - 1; j++){ps->arr[j] = ps->arr[j + 1];}ps->size--; // 元素数量减1// 不执行i++,因为下一个元素移动到了当前位置}else{i++; // 只有当没有删除元素时,才移动到下一个位置}}
}

4.6 查找指定数据


void SLFind(SL* ps, SLdate x)
{for (int i = 0;i < ps->size;i++){if (ps->arr[i] == x){printf("找到了 顺序表第%d个",i+1);printf("\n");}}
}

4.6 动态顺序表销毁

// 必须手动释放动态分配的内存,避免内存泄漏
void DynamicSeqListDestroy(DynamicSeqList* dsl) {free(dsl->data);dsl->data = NULL;dsl->size = 0;dsl->capacity = 0;
}

示例运行结果如下:

五. 优缺点分析

静态顺序表

  • 优点

    • 实现简单,无需处理复杂的内存分配逻辑

    • 访问速度快,无需额外的指针操作

    • 栈上分配内存,自动释放,不会造成内存泄漏

  • 缺点

    • 容量固定,无法适应数据量动态变化的场景

    • 可能造成内存空间浪费(当实际元素远少于最大容量时)

    • 存在溢出风险(当元素数量超过最大容量时)

动态顺序表

  • 优点

    • 容量可动态调整,能灵活适应数据量变化

    • 内存利用率高,按需分配空间

    • 不存在固定容量导致的溢出问题

  • 缺点

    • 实现相对复杂,需要处理扩容、内存分配等问题

    • 扩容时可能产生内存碎片

    • 需手动管理内存,若操作不当易造成内存泄漏

    • 扩容过程中需要拷贝数据,可能影响性能

六. 适用场景选择

  1. 静态顺序表适用场景

    • 数据量已知且固定不变的情况

    • 对内存管理要求简单,不希望手动处理内存分配的场景

    • 嵌入式系统、单片机等内存资源受限且数据量固定的环境

  2. 动态顺序表适用场景

    • 数据量不确定,需要动态增减的情况

    • 对内存利用率要求较高的场景

    • 大部分通用编程场景,如实现动态数组、列表等数据结构

七. 总结

静态顺序表和动态顺序表都是基于数组的线性表实现,核心区别在于存储空间是否可动态调整。静态顺序表简单直接但缺乏灵活性,动态顺序表灵活高效但实现稍复杂。

在实际开发中,动态顺序表应用更为广泛,因为它能更好地适应数据量动态变化的需求。了解两者的特点和差异,有助于根据具体场景选择合适的实现方式,从而优化程序性能和内存使用。

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

如若内容造成侵权/违法违规/事实不符,请联系英文站点网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

[Maven 基础课程]Maven 是什么

Maven 的官方网站&#xff1a;https://maven.apache.org/ 来自 Maven 官网的对于 Maven 是什么的描述&#xff1a; Apache Maven is a build tool for Java projects. Using a project object model (POM), Maven manages a project’s compilation, testing, and documentat…

【MATLAB例程】三维组合导航,滤波使用EKF,带严格的惯导推算、雅克比求解函数,图像对比滤波前后的速度、位置、姿态

文章目录程序介绍系统建模滤波框架仿真设置性能对比代码优点运行结果MATLAB源代码程序介绍 本程序实现了 三维状态量的扩展卡尔曼滤波&#xff08;EKF&#xff09;组合导航仿真&#xff0c;采用严格的15维误差状态模型&#xff0c;状态向量包括&#xff1a; x[pxpypzvxvyvzϕθ…

港资企业在大陆,如何靠 SD-WAN 专线畅连香港?

在当前市场形势下&#xff0c;港资企业在大陆的业务布局不断拓展&#xff0c;企业间访问香港总部系统以及香港员工到内陆出差时访问相关系统&#xff0c;成为日常运营的高频需求。然而&#xff0c;网络问题却常常阻碍业务的顺畅开展&#xff0c;基于 SD-WAN 专线的到香港加速网…

并发编程——08 Semaphore源码分析

1 概述Semaphore 是基于 AQS CAS 实现的&#xff0c;可根据构造参数的布尔值&#xff0c;选择使用公平锁&#xff0c;还是非公平锁。Semaphore 默认使用非公平锁&#xff1b;2 构造函数 // AQS的实现 private final Sync sync;// 默认使用非公平锁 public Semaphore(int permi…

Java全栈开发面试实战:从基础到微服务的深度解析

Java全栈开发面试实战&#xff1a;从基础到微服务的深度解析 一、面试开场 面试官&#xff08;中年工程师&#xff0c;穿着休闲但专业&#xff09;&#xff1a;你好&#xff0c;我是李工&#xff0c;今天来聊一下你的技术背景。你之前在XX科技做全栈开发&#xff0c;对吧&#…

CVPR深度学习论文创新合集拆解:模型训练速度算提升

关注gongzhonghao【CVPR顶会精选】大语言模型扩散Transformer的深度融合&#xff0c;让文本到图像生成更精准、细节更丰富&#xff1b;同时&#xff0c;专家轨迹正则化深度强化学习在自动对焦中的稳定加速表现&#xff0c;也展示了深度学习与轨迹建模结合的潜力。这样的组合正在…

【智能体】零代码学习 Coze 智能体(2)创建智能体的完整步骤

欢迎关注【AGI使用教程】 专栏 【智能体】零代码学习 Coze 智能体&#xff08;1&#xff09; 【智能体】零代码学习 Coze 智能体&#xff08;2&#xff09; 【智能体】零代码学习 Coze 智能体&#xff08;1&#xff09;1、登录 Coze 平台2、创建智能体3、智能体编排页面4、编写…

WPF和WinFrom区别

WPF 总结Windows Presentation Foundation (WPF) 是微软开发的一个用于构建 Windows 桌面应用程序的用户界面框架。它基于 .NET Framework&#xff0c;提供丰富的图形、动画和数据绑定功能&#xff0c;帮助开发者创建现代化、高性能的应用程序。以下是其核心要点总结&#xff1…

数据库原理及应用_数据库基础_第3章数据库编程_常用系统函数

前言 "<数据库原理及应用>(MySQL版)".以下称为"本书"中3.1.2节内容 引入 数据库常用系统函数的分析.上一篇帖子分析了,数据库函数需要看看能否被C语言函数替代 1.字符串函数 1)计算字符串字符数的函数和字符串长度的函数 语法: CHAR_LENGTH(str)…

回归问题的损失函数

简单来说&#xff0c;​在回归问题中&#xff0c;最常用的损失函数是均方误差&#xff08;MSE, Mean Squared Error&#xff09;和平均绝对误差&#xff08;MAE, Mean Absolute Error&#xff09;​。它们衡量的都是模型预测值&#xff08;ŷ&#xff09;与真实值&#xff08;y…

吴恩达机器学习(四)

一、神经网络神经元模拟逻辑单元&#xff1a;神经网络简单模型&#xff1a;神经网络中的前向传播过程&#xff1a;依次计算激活项&#xff0c;从输入层到隐藏层再到输出层的过程。样例&#xff1a;多元分类&#xff1a;

【重学 MySQL】九十三、MySQL的字符集的修改与底层原理详解

【重学 MySQL】九十三、MySQL的字符集的修改与底层原理详解一、字符集修改方法1. **配置文件修改**2. **SQL命令修改**3. **数据迁移方案**二、底层原理与注意事项1. **字符集与排序规则**2. **存储与性能影响**3. **数据一致性风险**三、常见问题解决1. **乱码问题**2. **性能…

pdf 转图片工具实现

一、安装 sudo yum install poppler-utils pdftoppm -v pdftoppm -png -r 300 a.pdf /tmp/page 运行效果&#xff1a; PDF转图片工具 - 在线PDF转PNG/JPG/TIFF转换器 | 免费在线工具 后台实现&#xff1a; using System.Diagnostics; using System.IO.Compression;namespac…

Zynq开发实践(FPGA之输入、输出整合)

【 声明&#xff1a;版权所有&#xff0c;欢迎转载&#xff0c;请勿用于商业用途。 联系信箱&#xff1a;feixiaoxing 163.com】fpga开发的时候习惯上先把功能拆分成若干个模块。针对这些模块&#xff0c;一个一、个实现好之后&#xff0c;再用wire连接即可。这一点有点像软件编…

【Linux基础】深入理解计算机启动原理:MBR主引导记录详解

目录 引言 1 硬盘分区初始化概述 1.1 为什么需要硬盘分区 1.2 硬盘分区格式的发展 1.3 分区初始化的基本流程 2 MBR详解 2.1 MBR的定义与位置 2.2 MBR的结构详解 2.3 分区表结构详解 2.4 MBR的工作原理 2.5 MBR的引导程序 3 MBR的局限性 3.1 硬盘容量限制 3.2 分…

Linux 线程同步

线程同步 由于线程共享内存&#xff0c;访问共享数据&#xff08;全局变量、堆内存&#xff09;必须进行同步&#xff0c;以防止竞态条件&#xff08;Race Conditions&#xff09;导致数据不一致或程序崩溃。 子线程没有独立的地址空间&#xff0c;数据通常是共享的&#xff1b…

世界模型的典型框架与分类

1.概述 人类和动物智能的一个重要方面是我们对世界的内部模型。我们使用这个模型来预测我们的行为将如何影响我们的环境&#xff0c;预测未来的事件&#xff0c;并计划复杂的行动序列以实现目标。当前大多数机器学习研究都集中在被动理解数据的模型上&#xff0c;例如图像分类…

【Day 35】Linux-Mysql错误总结

&#xff08;一&#xff09;MySQL 基础操作与服务故障类 连接层错误&#xff08;客户端与服务器的连接建立失败&#xff09; 解决 socket 路径、文件存在性及服务可用性问题。 1、MySQL 客户端连接失败&#xff08;报错 “Cant connect to local MySQL server throgh socket…

MYSQL速通(2/5)

六、多表查询1、多表关系①、一对多&#xff08;多对一&#xff09;举例&#xff1a;一个部门对多个员工实现&#xff1a;多的那边建立外键&#xff0c;指向一的那边的主键②、多对多举例&#xff1a;一个学生可选多门课&#xff0c;一门课可被多个学生选实现&#xff1a;建立中…

CRM、ERP、HRP系统有啥区别?

要理解CRM、ERP、HRP系统&#xff0c;需先明确三者的核心定位&#xff08;聚焦客户、企业全资源、特定领域资源&#xff09;&#xff0c;再从管理范围、目标、用户等维度区分。以下是详细解析&#xff1a; 一、各系统核心定义与核心模块 1. CRM系统&#xff1a;客户关系管理系统…